Wikiwordbōc angwiktionary https://ang.wiktionary.org/wiki/H%C4%93afodtramet MediaWiki 1.47.0-wmf.21 case-sensitive Media Syndrig Mōtung Brūcend Brūcendmōtung Wikiwordbōc Wikiwordbōcmōtung Ymele Ymelmōtung MediaWiki MediaWikimōtung Bysen Bysenmōtung Help Helpmōtung Flocc Floccmōtung Ætēaca Ætēacmōtung TimedText TimedText talk Module Module talk Event Event talk Wikiwordbōc:Ealuhūs 4 5774 54668 54620 2026-09-25T12:48:31Z MediaWiki message delivery 2721 /* Your feedback wanted: Remove (Language link added:) and similar edits from Special:RecentChanges */ new section 54668 wikitext text/x-wiki * ''Ealdhord: [[Wikiwordbōc:Ealuhūs/Hord 1|1]], [[Wikiwordbōc:Ealuhūs/Hord 2|2]], [[Wikiwordbōc:Ealuhūs/Hord 3|3]], [[Wikiwordbōc:Ealuhūs/Hord 4|4]], [[Wikiwordbōc:Ealuhūs/Hord 5|5]] == Launching! Join Us for Wiki Loves Ramadan 2025! == Dear All, We’re happy to announce the launch of [[m:Wiki Loves Ramadan 2025|Wiki Loves Ramadan 2025]], an annual international campaign dedicated to celebrating and preserving Islamic cultures and history through the power of Wikipedia. As an active contributor to the Local Wikipedia, you are specially invited to participate in the launch. This year’s campaign will be launched for you to join us write, edit, and improve articles that showcase the richness and diversity of Islamic traditions, history, and culture. * Topic: [[m:Event:Wiki Loves Ramadan 2025 Campaign Launch|Wiki Loves Ramadan 2025 Campaign Launch]] * When: Jan 19, 2025 * Time: 16:00 Universal Time UTC and runs throughout Ramadan (starting February 25, 2025). * Join Zoom Meeting: https://us02web.zoom.us/j/88420056597?pwd=NdrpqIhrwAVPeWB8FNb258n7qngqqo.1 * Zoom meeting hosted by [[m:Wikimedia Bangladesh|Wikimedia Bangladesh]] To get started, visit the [[m:Wiki Loves Ramadan 2025|campaign page]] for details, resources, and guidelines: Wiki Loves Ramadan 2025. Add [[m:Wiki Loves Ramadan 2025/Participant|your community here]], and organized Wiki Loves Ramadan 2025 in your local language. Whether you’re a first-time editor or an experienced Wikipedian, your contributions matter. Together, we can ensure Islamic cultures and traditions are well-represented and accessible to all. Feel free to invite your community and friends too. Kindly reach out if you have any questions or need support as you prepare to participate. Let’s make Wiki Loves Ramadan 2025 a success! For the [[m:Wiki Loves Ramadan 2025/Team|International Team]] 12:07, 16 Se Æfterra Gēola 2025 (UTC) <!-- Message sent by User:ZI Jony@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 --> == Universal Code of Conduct annual review: provide your comments on the UCoC and Enforcement Guidelines == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 3 February 2025. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]]. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 01:12, 24 Se Æfterra Gēola 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 --> == Reminder: first part of the annual UCoC review closes soon == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. This is a reminder that the first phase of the annual review period for the Universal Code of Conduct and Enforcement Guidelines will be closing soon. You can make suggestions for changes through [[d:Q614092|the end of day]], 3 February 2025. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. After review of the feedback, proposals for updated text will be published on Meta in March for another round of community review. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 00:49, 3 Solmonað 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28198931 --> == <span lang="en" dir="ltr"> Upcoming Language Community Meeting (Feb 28th, 14:00 UTC) and Newsletter</span> == <div lang="en" dir="ltr"> <section begin="message"/> Hello everyone! [[File:WP20Symbols WIKI INCUBATOR.svg|right|frameless|150x150px|alt=An image symbolising multiple languages]] We’re excited to announce that the next '''Language Community Meeting''' is happening soon, '''February 28th at 14:00 UTC'''! If you’d like to join, simply sign up on the '''[[mw:Wikimedia_Language_and_Product_Localization/Community_meetings#28_February_2025|wiki page]]'''. This is a participant-driven meeting where we share updates on language-related projects, discuss technical challenges in language wikis, and collaborate on solutions. In our last meeting, we covered topics like developing language keyboards, creating the Moore Wikipedia, and updates from the language support track at Wiki Indaba. '''Got a topic to share?''' Whether it’s a technical update from your project, a challenge you need help with, or a request for interpretation support, we’d love to hear from you! Feel free to '''reply to this message''' or add agenda items to the document '''[[etherpad:p/language-community-meeting-feb-2025|here]]'''. Also, we wanted to highlight that the sixth edition of the Language & Internationalization newsletter (January 2025) is available here: [[:mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Wikimedia Language and Product Localization/Newsletter/2025/January]]. This newsletter provides updates from the October–December 2024 quarter on new feature development, improvements in various language-related technical projects and support efforts, details about community meetings, and ideas for contributing to projects. To stay updated, you can subscribe to the newsletter on its wiki page: [[:mw:Wikimedia Language and Product Localization/Newsletter|Wikimedia Language and Product Localization/Newsletter]]. We look forward to your ideas and participation at the language community meeting, see you there! <section end="message"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 08:30, 22 Solmonað 2025 (UTC) <!-- Message sent by User:SSethi (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28217779 --> == <span lang="en" dir="ltr">Migration to Parsoid</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> Hello everyone! I am glad to inform you that as the next step in the [[mw:Special:MyLanguage/Parsoid/Parser Unification|Parser Unification]] project, Parsoid will soon be turned on as the default article renderer on your Wiktionary. We are gradually increasing the number of wikis using Parsoid, with the intention of making it the default wikitext parser for MediaWiki's next long-term support release. This will make our wikis more reliable and consistent for editors, readers, and tools to use, as well as making the development of future wikitext features easier. If this transition disrupts your workflow, don’t worry! You can still opt out through a user preference or turn Parsoid off on the current page using the Tools submenu, as described in the [[mw:Special:MyLanguage/Help:Extension:ParserMigration|Extension:ParserMigration]] documentation. There is [[mw:Special:MyLanguage/Parsoid/Parser_Unification/Confidence_Framework|more information about our roll-out strategy]] available, including the testing done before we turn on Parsoid for a new wiki. To report bugs and issues, please look at our [[mw:Special:MyLanguage/Parsoid/Parser Unification/Known Issues|known issues]] documentation and if you found a new bug please create a phab ticket and tag the [[phab:project/view/5846|Content Transform Team in Phabricator]]. <section end="announcement-content" /> </div> <section begin="signature"/> <em>[[m:Special:MyLanguage/Wikimedia Foundation/Product and Technology/Parsoid Read Views/Wiktionary Announcement|{{MediaWiki:Please-translate}}]]</em> <bdi lang="en" dir="ltr">[[mw:User:ABreault (WMF)|Content Transform Team]]</bdi> 00:24, 5 Hreðmonað 2025 (UTC) <section end="signature"/> <!-- Message sent by User:ABreault (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Wikimedia_Foundation/Product_and_Technology/Parsoid_Read_Views/2025-03-05_Wiktionaries&oldid=28340212 --> == Universal Code of Conduct annual review: proposed changes are available for comment == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. I am writing to you to let you know that [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|proposed changes]] to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct (UCoC) Enforcement Guidelines]] and [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|Universal Code of Conduct Coordinating Committee (U4C) Charter]] are open for review. '''[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|You can provide feedback on suggested changes]]''' through the [[d:Q614092|end of day]] on Tuesday, 18 March 2025. This is the second step in the annual review process, the final step will be community voting on the proposed changes. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find relevant links about the process on the UCoC annual review page on Meta]]. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]]. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] 18:52, 7 Hreðmonað 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28307738 --> == <span lang="en" dir="ltr">Your wiki will be in read-only soon</span> == <div lang="en" dir="ltr"> <section begin="server-switch"/><div class="plainlinks"> [[:m:Special:MyLanguage/Tech/Server switch|Read this message in another language]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}] The [[foundation:|Wikimedia Foundation]] will switch the traffic between its data centers. This will make sure that Wikipedia and the other Wikimedia wikis can stay online even after a disaster. All traffic will switch on '''{{#time:j xg|2025-03-19|en}}'''. The switch will start at '''[https://zonestamp.toolforge.org/{{#time:U|2025-03-19T14:00|en}} {{#time:H:i e|2025-03-19T14:00}}]'''. Unfortunately, because of some limitations in [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], all editing must stop while the switch is made. We apologize for this disruption, and we are working to minimize it in the future. A banner will be displayed on all wikis 30 minutes before this operation happens. This banner will remain visible until the end of the operation. '''You will be able to read, but not edit, all wikis for a short period of time.''' *You will not be able to edit for up to an hour on {{#time:l j xg Y|2025-03-19|en}}. *If you try to edit or save during these times, you will see an error message. We hope that no edits will be lost during these minutes, but we can't guarantee it. If you see the error message, then please wait until everything is back to normal. Then you should be able to save your edit. But, we recommend that you make a copy of your changes first, just in case. ''Other effects'': *Background jobs will be slower and some may be dropped. Red links might not be updated as quickly as normal. If you create an article that is already linked somewhere else, the link will stay red longer than usual. Some long-running scripts will have to be stopped. * We expect the code deployments to happen as any other week. However, some case-by-case code freezes could punctually happen if the operation require them afterwards. * [[mw:Special:MyLanguage/GitLab|GitLab]] will be unavailable for about 90 minutes. This project may be postponed if necessary. You can [[wikitech:Switch_Datacenter|read the schedule at wikitech.wikimedia.org]]. Any changes will be announced in the schedule. '''Please share this information with your community.'''</div><section end="server-switch"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23:15, 14 Hreðmonað 2025 (UTC) <!-- Message sent by User:Quiddity (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=28307742 --> == Final proposed modifications to the Universal Code of Conduct Enforcement Guidelines and U4C Charter now posted == <div lang="en" dir="ltr" class="mw-content-ltr"> The proposed modifications to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct Enforcement Guidelines]] and the U4C Charter [[m:Universal_Code_of_Conduct/Annual_review/2025/Proposed_Changes|are now on Meta-wiki for community notice]] in advance of the voting period. This final draft was developed from the previous two rounds of community review. Community members will be able to vote on these modifications starting on 17 April 2025. The vote will close on 1 May 2025, and results will be announced no later than 12 May 2025. The U4C election period, starting with a call for candidates, will open immediately following the announcement of the review results. More information will be posted on [[m:Special:MyLanguage//Universal_Code_of_Conduct/Coordinating_Committee/Election|the wiki page for the election]] soon. Please be advised that this process will require more messages to be sent here over the next two months. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]]. Please share this message with members of your community so they can participate as well. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 02:05, 4 Eastermonað 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 --> == Wikidata and Sister Projects: An online community event == ''(Apologies for posting in English)'' Hello everyone, I am excited to share news of an upcoming online event called '''[[d:Event:Wikidata_and_Sister_Projects|Wikidata and Sister Projects]]''' celebrating the different ways Wikidata can be used to support or enhance with another Wikimedia project. The event takes place over 4 days between '''May 29 - June 1st, 2025'''. We would like to invite speakers to present at this community event, to hear success stories, challenges, showcase tools or projects you may be working on, where Wikidata has been involved in Wikipedia, Commons, WikiSource and all other WM projects. If you are interested in attending, please [[d:Special:RegisterForEvent/1291|register here]]. If you would like to speak at the event, please fill out this Session Proposal template on the [[d:Event_talk:Wikidata_and_Sister_Projects|event talk page]], where you can also ask any questions you may have. I hope to see you at the event, in the audience or as a speaker, - [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 09:18, 11 Eastermonað 2025 (UTC) <!-- Message sent by User:Danny Benjafield (WMDE)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Send_List&oldid=28525705 --> == Vote now on the revised UCoC Enforcement Guidelines and U4C Charter == <div lang="en" dir="ltr" class="mw-content-ltr"> The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines ("UCoC EG") and the UCoC's Coordinating Committee Charter is open now through the end of 1 May (UTC) ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/2025/Voter_information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review of the EG and Charter was planned and implemented by the U4C. Further information will be provided in the coming months about the review of the UCoC itself. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]]. Please share this message with members of your community so they can participate as well. In cooperation with the U4C -- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 00:35, 17 Eastermonað 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 --> == <span lang="en" dir="ltr">Vote on proposed modifications to the UCoC Enforcement Guidelines and U4C Charter</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines and U4C Charter closes on 1 May 2025 at 23:59 UTC ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025/Voter information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki. The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|review the U4C Charter]]. Please share this message with members of your community in your language, as appropriate, so they can participate as well. In cooperation with the U4C -- <section end="announcement-content" /> </div> <div lang="en" dir="ltr" class="mw-content-ltr"> [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 03:42, 29 Eastermonað 2025 (UTC)</div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> == We will be enabling the new Charts extension on your wiki soon! == ''(Apologies for posting in English)'' Hi all! We have good news to share regarding the ongoing problem with graphs and charts affecting all wikis that use them. As you probably know, the [[:mw:Special:MyLanguage/Extension:Graph|old Graph extension]] was disabled in 2023 [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/EWL4AGBEZEDMNNFTM4FRD4MHOU3CVESO/|due to security reasons]]. We’ve worked in these two years to find a solution that could replace the old extension, and provide a safer and better solution to users who wanted to showcase graphs and charts in their articles. We therefore developed the [[:mw:Special:MyLanguage/Extension:Chart|Charts extension]], which will be replacing the old Graph extension and potentially also the [[:mw:Extension:EasyTimeline|EasyTimeline extension]]. After successfully deploying the extension on Italian, Swedish, and Hebrew Wikipedia, as well as on MediaWiki.org, as part of a pilot phase, we are now happy to announce that we are moving forward with the next phase of deployment, which will also include your wiki. The deployment will happen in batches, and will start from '''May 6'''. Please, consult [[:mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|our page on MediaWiki.org]] to discover when the new Charts extension will be deployed on your wiki. You can also [[:mw:Special:MyLanguage/Extension:Chart|consult the documentation]] about the extension on MediaWiki.org. If you have questions, need clarifications, or just want to express your opinion about it, please refer to the [[:mw:Special:MyLanguage/Extension_talk:Chart/Project|project’s talk page on Mediawiki.org]], or ping me directly under this thread. If you encounter issues using Charts once it gets enabled on your wiki, please report it on the [[:mw:Extension_talk:Chart/Project|talk page]] or at [[phab:tag/charts|Phabricator]]. Thank you in advance! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 15:08, 6 Þrimilcemonað 2025 (UTC) <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28663781 --> == <span lang="en" dir="ltr">Call for Candidates for the Universal Code of Conduct Coordinating Committee (U4C)</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> The results of voting on the Universal Code of Conduct Enforcement Guidelines and Universal Code of Conduct Coordinating Committee (U4C) Charter is [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025#Results|available on Meta-wiki]]. You may now [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|submit your candidacy to serve on the U4C]] through 29 May 2025 at 12:00 UTC. Information about [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025|eligibility, process, and the timeline are on Meta-wiki]]. Voting on candidates will open on 1 June 2025 and run for two weeks, closing on 15 June 2025 at 12:00 UTC. If you have any questions, you can ask on [[m:Talk:Universal Code of Conduct/Coordinating Committee/Election/2025|the discussion page for the election]]. -- in cooperation with the U4C, </div><section end="announcement-content" /> </div> <bdi lang="en" dir="ltr">[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|motung]])</bdi> 22:08, 15 Þrimilcemonað 2025 (UTC) <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> == RfC ongoing regarding Abstract Wikipedia (and your project) == <div lang="en" dir="ltr" class="mw-content-ltr"> ''(Apologies for posting in English, if this is not your first language)'' Hello all! We opened a discussion on Meta about a very delicate issue for the development of [[:m:Special:MyLanguage/Abstract Wikipedia|Abstract Wikipedia]]: where to store the abstract content that will be developed through functions from Wikifunctions and data from Wikidata. Since some of the hypothesis involve your project, we wanted to hear your thoughts too. We want to make the decision process clear: we do not yet know which option we want to use, which is why we are consulting here. We will take the arguments from the Wikimedia communities into account, and we want to consult with the different communities and hear arguments that will help us with the decision. The decision will be made and communicated after the consultation period by the Foundation. You can read the various hypothesis and have your say at [[:m:Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]]. Thank you in advance! -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 15:27, 22 Þrimilcemonað 2025 (UTC) </div> <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28768453 --> == <span lang="en" dir="ltr">Wikimedia Foundation Board of Trustees 2025 Selection & Call for Questions</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> :''[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]'' Dear all, This year, the term of 2 (two) Community- and Affiliate-selected Trustees on the Wikimedia Foundation Board of Trustees will come to an end [1]. The Board invites the whole movement to participate in this year’s selection process and vote to fill those seats. The Elections Committee will oversee this process with support from Foundation staff [2]. The Governance Committee, composed of trustees who are not candidates in the 2025 community-and-affiliate-selected trustee selection process (Raju Narisetti, Shani Evenstein Sigalov, Lorenzo Losa, Kathy Collins, Victoria Doronina and Esra’a Al Shafei) [3], is tasked with providing Board oversight for the 2025 trustee selection process and for keeping the Board informed. More details on the roles of the Elections Committee, Board, and staff are here [4]. Here are the key planned dates: * May 22 – June 5: Announcement (this communication) and call for questions period [6] * June 17 – July 1, 2025: Call for candidates * July 2025: If needed, affiliates vote to shortlist candidates if more than 10 apply [5] * August 2025: Campaign period * August – September 2025: Two-week community voting period * October – November 2025: Background check of selected candidates * Board’s Meeting in December 2025: New trustees seated Learn more about the 2025 selection process - including the detailed timeline, the candidacy process, the campaign rules, and the voter eligibility criteria - on this Meta-wiki page [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025|[link]]]. '''Call for Questions''' In each selection process, the community has the opportunity to submit questions for the Board of Trustees candidates to answer. The Election Committee selects questions from the list developed by the community for the candidates to answer. Candidates must answer all the required questions in the application in order to be eligible; otherwise their application will be disqualified. This year, the Election Committee will select 5 questions for the candidates to answer. The selected questions may be a combination of what’s been submitted from the community, if they’re alike or related. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025/Questions_for_candidates|[link]]] '''Election Volunteers''' Another way to be involved with the 2025 selection process is to be an Election Volunteer. Election Volunteers are a bridge between the Elections Committee and their respective community. They help ensure their community is represented and mobilize them to vote. Learn more about the program and how to join on this Meta-wiki page [[m:Wikimedia_Foundation_elections/2025/Election_volunteers|[link].]] Thank you! [1] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2022/Results [2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter [3] https://foundation.wikimedia.org/wiki/Resolution:Committee_Membership,_December_2024 [4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles [5] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/FAQ [6] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/Questions_for_candidates Best regards, Victoria Doronina Board Liaison to the Elections Committee Governance Committee<section end="announcement-content" /> </div> [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 03:08, 28 Þrimilcemonað 2025 (UTC) <!-- Message sent by User:RamzyM (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> == Vote now in the 2025 U4C Election == <div lang="en" dir="ltr" class="mw-content-ltr"> Apologies for writing in English. {{Int:Please-translate}} Eligible voters are asked to participate in the 2025 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2025|2025 Election information page]]. The vote closes on 17 June 2025 at [https://zonestamp.toolforge.org/1750161600 12:00 UTC]. Please vote if your account is eligible. Results will be available by 1 July 2025. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 23:01, 13 Sēremōnaþ 2025 (UTC) </div> <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28848819 --> == <span lang="en" dir="ltr">Wikimedia Foundation Board of Trustees 2025 - Call for Candidates</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> :''<div class="plainlinks">[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Call for candidates|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Call for candidates}}&language=&action=page&filter= {{int:please-translate}}]</div> Hello all, The [[m:Special:MyLanguage/Wikimedia Foundation elections/2025|call for candidates for the 2025 Wikimedia Foundation Board of Trustees selection is now open]] from June 17, 2025 – July 2, 2025 at 11:59 UTC [1]. The Board of Trustees oversees the Wikimedia Foundation's work, and each Trustee serves a three-year term [2]. This is a volunteer position. This year, the Wikimedia community will vote in late August through September 2025 to fill two (2) seats on the Foundation Board. Could you – or someone you know – be a good fit to join the Wikimedia Foundation's Board of Trustees? [3] Learn more about what it takes to stand for these leadership positions and how to submit your candidacy on [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Candidate application|this Meta-wiki page]] or encourage someone else to run in this year's election. Best regards, Abhishek Suryawanshi<br /> Chair of the Elections Committee On behalf of the Elections Committee and Governance Committee [1] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Call_for_candidates [2] https://foundation.wikimedia.org/wiki/Legal:Bylaws#(B)_Term. [3] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Resources_for_candidates<section end="announcement-content" /> </div> [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 17:44, 17 Sēremōnaþ 2025 (UTC) <!-- Message sent by User:RamzyM (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28866958 --> == <span lang="en" dir="ltr">Sister Projects Task Force reviews Wikispore and Wikinews</span> == <div lang="en" dir="ltr"> <section begin="message"/> Dear Wikimedia Community, The [[m:Wikimedia Foundation Community Affairs Committee|Community Affairs Committee (CAC)]] of the Wikimedia Foundation Board of Trustees assigned [[m:Wikimedia Foundation Community Affairs Committee/Sister Projects Task Force|the Sister Projects Task Force (SPTF)]] to update and implement a procedure for assessing the lifecycle of Sister Projects – wiki [[m:Wikimedia projects|projects supported by Wikimedia Foundation (WMF)]]. A vision of relevant, accessible, and impactful free knowledge has always guided the Wikimedia Movement. As the ecosystem of Wikimedia projects continues to evolve, it is crucial that we periodically review existing projects to ensure they still align with our goals and community capacity. Despite their noble intent, some projects may no longer effectively serve their original purpose. '''Reviewing such projects is not about giving up – it's about responsible stewardship of shared resources'''. Volunteer time, staff support, infrastructure, and community attention are finite, and the non-technical costs tend to grow significantly as our ecosystem has entered a different age of the internet than the one we were founded in. Supporting inactive projects or projects that didn't meet our ambitions can unintentionally divert these resources from areas with more potential impact. Moreover, maintaining projects that no longer reflect the quality and reliability of the Wikimedia name stands for, involves a reputational risk. An abandoned or less reliable project affects trust in the Wikimedia movement. Lastly, '''failing to sunset or reimagine projects that are no longer working can make it much harder to start new ones'''. When the community feels bound to every past decision – no matter how outdated – we risk stagnation. A healthy ecosystem must allow for evolution, adaptation, and, when necessary, letting go. If we create the expectation that every project must exist indefinitely, we limit our ability to experiment and innovate. Because of this, SPTF reviewed two requests concerning the lifecycle of the Sister Projects to work through and demonstrate the review process. We chose Wikispore as a case study for a possible new Sister Project opening and Wikinews as a case study for a review of an existing project. Preliminary findings were discussed with the CAC, and a community consultation on both proposals was recommended. === Wikispore === The [[m:Wikispore|application to consider Wikispore]] was submitted in 2019. SPTF decided to review this request in more depth because rather than being concentrated on a specific topic, as most of the proposals for the new Sister Projects are, Wikispore has the potential to nurture multiple start-up Sister Projects. After careful consideration, the SPTF has decided '''not to recommend''' Wikispore as a Wikimedia Sister Project. Considering the current activity level, the current arrangement allows '''better flexibility''' and experimentation while WMF provides core infrastructural support. We acknowledge the initiative's potential and seek community input on what would constitute a sufficient level of activity and engagement to reconsider its status in the future. As part of the process, we shared the decision with the Wikispore community and invited one of its leaders, Pharos, to an SPTF meeting. Currently, we especially invite feedback on measurable criteria indicating the project's readiness, such as contributor numbers, content volume, and sustained community support. This would clarify the criteria sufficient for opening a new Sister Project, including possible future Wikispore re-application. However, the numbers will always be a guide because any number can be gamed. === Wikinews === We chose to review Wikinews among existing Sister Projects because it is the one for which we have observed the highest level of concern in multiple ways. Since the SPTF was convened in 2023, its members have asked for the community's opinions during conferences and community calls about Sister Projects that did not fulfil their promise in the Wikimedia movement.[https://commons.wikimedia.org/wiki/File:WCNA_2024._Sister_Projects_-_opening%3F_closing%3F_merging%3F_splitting%3F.pdf <nowiki>[1]</nowiki>][https://meta.wikimedia.org/wiki/Wikimedia_Foundation_Community_Affairs_Committee/Sister_Projects_Task_Force#Wikimania_2023_session_%22Sister_Projects:_past,_present_and_the_glorious_future%22 <nowiki>[2]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F <nowiki>[3]</nowiki>] Wikinews was the leading candidate for an evaluation because people from multiple language communities proposed it. Additionally, by most measures, it is the least active Sister Project, with the greatest drop in activity over the years. While the Language Committee routinely opens and closes language versions of the Sister Projects in small languages, there has never been a valid proposal to close Wikipedia in major languages or any project in English. This is not true for Wikinews, where there was a proposal to close English Wikinews, which gained some traction but did not result in any action[https://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_English_Wikinews <nowiki>[4]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F <nowiki>[5]</nowiki>, see section 5] as well as a draft proposal to close all languages of Wikinews[https://meta.wikimedia.org/wiki/Talk:Proposals_for_closing_projects/Archive_2#Close_Wikinews_completely,_all_languages? <nowiki>[6]</nowiki>]. [[:c:File:Sister Projects Taskforce Wikinews review 2024.pdf|Initial metrics]] compiled by WMF staff also support the community's concerns about Wikinews. Based on this report, SPTF recommends a community reevaluation of Wikinews. We conclude that its current structure and activity levels are the lowest among the existing sister projects. SPTF also recommends pausing the opening of new language editions while the consultation runs. SPTF brings this analysis to a discussion and welcomes discussions of alternative outcomes, including potential restructuring efforts or integration with other Wikimedia initiatives. '''Options''' mentioned so far (which might be applied to just low-activity languages or all languages) include but are not limited to: *Restructure how Wikinews works and is linked to other current events efforts on the projects, *Merge the content of Wikinews into the relevant language Wikipedias, possibly in a new namespace, *Merge content into compatibly licensed external projects, *Archive Wikinews projects. Your insights and perspectives are invaluable in shaping the future of these projects. We encourage all interested community members to share their thoughts on the relevant discussion pages or through other designated feedback channels. === Feedback and next steps === We'd be grateful if you want to take part in a conversation on the future of these projects and the review process. We are setting up two different project pages: [[m:Public consultation about Wikispore|Public consultation about Wikispore]] and [[m:Public consultation about Wikinews|Public consultation about Wikinews]]. Please participate between 27 June 2025 and 27 July 2025, after which we will summarize the discussion to move forward. You can write in your own language. I will also host a community conversation 16th July Wednesday 11.00 UTC and 17th July Thursday 17.00 UTC (call links to follow shortly) and will be around at Wikimania for more discussions. <section end="message"/> </div> -- [[User:Victoria|Victoria]] on behalf of the Sister Project Task Force, 20:57, 27 Sēremōnaþ 2025 (UTC) <!-- Message sent by User:Johan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Johan_(WMF)/Sister_project_MassMassage_on_behalf_of_Victoria/Target_list&oldid=28911188 --> == Wikidata Item and Property labels soon displayed in Wiki Watchlist/Recent Changes == ''(Apologies for posting in English, you can help by translating into your language)'' Hello everyone, the [[m:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|Wikidata For Wikimedia Projects]] team is excited to announce an upcoming change in how Wikidata edit changelogs are displayed in your [[Special:Watchlist|Watchlists]] and [[Special:RecentChanges|Recent Changes]] lists. If an edit is made on Wikidata that affects a page in another Wikimedia Project, the changelog will contain some information about the nature of the edit. This can include a QID (or Q-number), a PID (or P-number) and a value (which can be text, numbers, dates, or also QID or PID’s). Confused by these terms? See the [[d:Special:MyLanguage/Wikidata:Glossary|Wikidata:Glossary]] for further explanations. The upcoming change is scheduled for '''17.07.2025''', between '''1300 - 1500 UTC'''. The change will display the label (item name) alongside any QID or PIDs, as seen in the image below: [[File:Apr10 edit summary on Wikidata.png|An edit sum entry on Wikidata, labels display alongside their P- and Q-no.'s]] These changes will only be visible if you have Wikidata edits enabled in your User Preferences for Watchlists and Recent Changes, or have the active filter ‘Wikidata edits’ checkbox toggled on, directly on the Watchlist and Recent Changes pages. Your bot and gadget may be affected! There are thousands of bots, gadgets and user-scripts and whilst we have researched potential effects to many of them, we cannot guarantee there won’t be some that are broken or affected by this change. Further information and context about this change, including how your bot may be affected can be found on this [[m:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|project task page]]. We welcome your questions and feedback, please write to us on this dedicated [[m:Talk:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Resolve_Labels|Talk page]]. Thank you, - [[m:User:Danny_Benjafield_(WMDE)|Danny Benjafield (WMDE)]] on behalf of the Wikidata For Wikimedia Projects Team. [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 12:46, 14 Æfterra Liða 2025 (UTC) <!-- Message sent by User:Danny Benjafield (WMDE)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Test_List&oldid=28981877 --> == Wikifunctions will be deployed on your wiki on August 27 == <div lang="en" dir="ltr" class="mw-content-ltr"> ''Please, translate this message into your language.'' Hi all, we want to let you know that [[:f:|Wikifunctions]] is coming to your project soon! When enabled, you will be able to call functions from your project, and integrate them in your articles. A function is something that takes one or more inputs and transforms them into a desired output. Think of adding up two numbers, or converting miles into metres, or calculating how much time has passed since an event, or declining a word into a case. This is usually done with templates that are complicated to create or to import. With Wikifunctions, you will be able to do this with just a couple of clicks! Please check out [[:f:Wikifunctions:Introduction|these tutorials]] for more details. You’re also invited to [[:f:Special:CreateObject|create functions]] that you find useful, or [[:f:Wikifunctions:Suggest a function|ask for help from the Wikifunctions community]]. We would like to invite you to contribute to Wikifunctions, by translating the existing functions labels into your language, so that more users in your community can more easily reuse them on the project. You can also translate the messages for the Wikifunctions interface on TranslateWiki ([https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-wikilambda-ve&filter=%21translated&action=translate here for VisualEditor messages], and [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-wikilambda-user&filter=%21translated&action=translate here for the Wikifunctions interface proper]). Of course, we are happy to help in case there are questions or difficulties, and we are ready to listen to your feedback. Please ping me directly in case of necessity or reach out to me [[User talk:Sannita (WMF)|on my talk page]]. -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:25, 13 Weodmonað 2025 (UTC) </div> <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=29116867 --> == <span lang="en" dir="ltr">Temporary accounts will be rolled out soon</span> == <div lang="en" dir="ltr"> <section begin="body"/> Hello, we are the Wikimedia Foundation [[mw:Special:MyLanguage/Product Safety and Integrity|Product Safety and Integrity]] team. We would like to announce that '''we plan to enable [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|temporary accounts]] for this wiki in the week of September 1'''. Temporary accounts are successfully live on 30 wikis, including many large ones like German, Japanese, and French. The change they bring is especially relevant to logged-out editors, who this feature is designed to protect. But it is also relevant to community members like mentors, patrollers, and admins – anyone who reverts edits, blocks users, or otherwise interacts with logged-out editors as part of keeping the wikis safe and accurate. '''Why we are building temporary accounts''' Our wikis should be safer to edit by default for logged-out editors. Temporary accounts allow people to continue editing the wikis without creating an account, while avoiding publicly tying their edits to their IP address. We believe this is in the best interest of our logged-out editors, who make valuable contributions to the wikis and who may later create accounts and grow our community of editors, admins, and other roles. Even though the wikis do warn logged-out editors that their IP address will be associated with their edit, many people may not understand what an IP address is, or that it could be used to connect them to other information about them in ways they might not expect. Additionally, our moderation software and tools rely too heavily on network origin (IP addresses) to identify users and patterns of activity, especially as IP addresses themselves are becoming less stable as identifiers. Temporary accounts allow for more precise interactions with logged-out editors, including more precise blocks, and can help limit how often we unintentionally end up blocking good-faith users who use the same IP addresses as bad-faith users. '''How temporary accounts work''' [[File:Temporary account banner and empty talk page.png|thumb]] Any time a logged-out user publishes an edit on this wiki, a cookie will be set in this user's browser, and a temporary account tied with this cookie will be automatically created. This account's name will follow the pattern: <code dir=ltr>~2025-12345-67</code> (a tilde, current year, a number). On pages like Recent Changes or page history, this name will be displayed. The cookie will expire 90 days after its creation. As long as it exists, all edits made from this device will be attributed to this temporary account. It will be the same account even if the IP address changes, unless the user clears their cookies or uses a different device or web browser. A record of the IP address used at the time of each edit will be stored for 90 days after the edit. However, only some logged-in users will be able to see it. '''What does this mean for different groups of users?''' '''For logged-out editors''' * This increases privacy: currently, if you do not use a registered account to edit, then everybody can see the IP address for the edits you made, even after 90 days. That will no longer be possible on this wiki. * If you use a temporary account to edit from different locations in the last 90 days (for example at home and at a coffee shop), the edit history and the IP addresses for all those locations will now be recorded together, for the same temporary account. Users who [[foundation:Special:MyLanguage/Policy:Access_to_temporary_account_IP_addresses|meet the relevant requirements]] will be able to view this data. If this creates any personal security concerns for you, please contact talktohumanrights at wikimedia.org for advice. '''For community members interacting with logged-out editors''' * A temporary account is uniquely linked to a device. In comparison, an IP address can be shared with different devices and people (for example, different people at school or at work might have the same IP address). * Compared to the current situation, it will be safer to assume that a temporary user's talk page belongs to only one person, and messages left there will be read by them. As you can see in the screenshot, temporary account users will receive notifications. It will also be possible to thank them for their edits, ping them in discussions, and invite them to get more involved in the community. '''For users who use IP address data to moderate and maintain the wiki''' * '''For patrollers''' who track persistent abusers, investigate violations of policies, etc.: Users who [[foundation:Special:MyLanguage/Policy:Access_to_temporary_account_IP_addresses|meet the requirements]] will be able to reveal temporary users' IP addresses and all contributions made by temporary accounts from a specific IP address or range ([[Special:IPContributions]]). They will also have access to useful information about the IP addresses thanks to the [[mw:Special:MyLanguage/Trust and Safety Product/IP Info|IP Info]] feature. Many other pieces of software have been built or adjusted to work with temporary accounts, including AbuseFilter, global blocks, Global User Contributions, and more. (For information for volunteer developers on how to update the code of your tools – see the last part of the message.) * '''For admins blocking logged-out editors''': ** It will be possible to block many abusers by just blocking their temporary accounts. A blocked person won't be able to create new temporary accounts quickly if the admin selects the [[mw:Special:MyLanguage/Autoblock|autoblock]] option. ** It will still be possible to block an IP address or IP range. * Temporary accounts will not be retroactively applied to contributions made before the deployment. On Special:Contributions, you will be able to see existing IP user contributions, but not new contributions made by temporary accounts on that IP address. Instead, you should use Special:IPContributions for this. '''Our requests for you, and next steps''' * If you know of any tools, bots, gadgets etc. using data about IP addresses or being available for logged-out users, you may want to test if they work on [[testwiki:Main_Page|testwiki]] or [[test2wiki:Main_Page|test2wiki]]. If you are a volunteer developer, [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers|read our documentation for developers]], and in particular, the section on [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/For developers#How should I update my code?|how your code might need to be updated]]. * If you want to test the temporary account experience, for example just to check what it feels like, go to testwiki or test2wiki and edit without logging in. * Tell us if you know of any difficulties that need to be addressed. We will try to help, and if we are not able, we will consider the available options. * Look at our [[m:Meta:Babel#Temporary_Accounts:_access_to_IP_addresses_and_next_steps|previous message]] about requirements for users without extended rights who may need access to IP addresses. To learn more about the project, check out [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/FAQ|our FAQ]] – you will find many useful answers there. You may also [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|look at the updates]] (we have just posted one) and [[mw:Newsletter:Product Safety and Integrity|subscribe to our new newsletter]]. If you'd like to talk to me (Szymon) off-wiki, you will find me on Discord and Telegram. Thank you!<section end="body" /> </div> <bdi lang="en" dir="ltr">[[m:user:NKohli (WMF)|NKohli (WMF)]], [[m:user:SGrabarczuk (WMF)|SGrabarczuk (WMF)]]</bdi> 21:37, 26 Weodmonað 2025 (UTC) <!-- Message sent by User:Quiddity (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Quiddity_(WMF)/sandbox6&oldid=29181713 --> == <span lang="en" dir="ltr">Server switch - Your wiki will be read-only for a short time soon</span> == <div lang="en" dir="ltr"> <section begin="server-switch"/><div class="plainlinks"> [[:m:Special:MyLanguage/Tech/Server switch|Read this message in another language]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}] The [[foundation:|Wikimedia Foundation]] will switch the traffic between its data centers. This will make sure that Wikipedia and the other Wikimedia wikis can stay online even after a disaster. All traffic will switch on '''{{#time:j xg|2025-09-24|en}}'''. The switch will start at '''[https://zonestamp.toolforge.org/{{#time:U|2025-09-24T15:00|en}} {{#time:H:i e|2025-09-24T15:00}}]'''. Unfortunately, because of some limitations in [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], all editing must stop while the switch is made. We apologize for this disruption, and we are working to minimize it in the future. A banner will be displayed on all wikis 30 minutes before this operation happens. This banner will remain visible until the end of the operation. You can contribute to the [https://meta.wikimedia.org/w/index.php?title=Special%3ATranslate&group=Centralnotice-tgroup-read_only_banner&task=view&language=&filter=&action=translate translation or proofreading] of this banner text. '''You will be able to read, but not edit, all wikis for a short period of time.''' *You will not be able to edit for up to an hour on {{#time:l j xg Y|2025-09-24|en}}. *If you try to edit or save during these times, you will see an error message. We hope that no edits will be lost during these minutes, but we can't guarantee it. If you see the error message, then please wait until everything is back to normal. Then you should be able to save your edit. But, we recommend that you make a copy of your changes first, just in case. ''Other effects'': *Background jobs will be slower and some may be dropped. Red links might not be updated as quickly as normal. If you create an article that is already linked somewhere else, the link will stay red longer than usual. Some long-running scripts will have to be stopped. * We expect the code deployments to happen as any other week. However, some case-by-case code freezes could punctually happen if the operation require them afterwards. * [[mw:Special:MyLanguage/GitLab|GitLab]] will be unavailable for about 90 minutes. This project may be postponed if necessary. You can [[wikitech:Switch_Datacenter|read the schedule at wikitech.wikimedia.org]]. Any changes will be announced in the schedule. '''Please share this information with your community.'''</div><section end="server-switch"/> </div> <span dir=ltr>[[m:User:Trizek (WMF)|Trizek (WMF)]] ([[m:User talk:Trizek (WMF)|{{int:talk}}]])</span> 15:42, 18 Haligmonað 2025 (UTC) <!-- Message sent by User:Trizek (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29170715 --> == <span lang="en" dir="ltr">Have your say: vote for the 2025 Board of Trustees</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> Hello all, The voting period for the [[m:Special:MyLanguage/Wikimedia Foundation elections/2025|2025 Board of Trustees election]] is now open. Candidates are running for two (2) seats on the Board. To check your voter eligibility, please visit the [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Voter eligibility guidelines|voter eligibility page]]. Learn more about them by [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Candidates|reading their application statements and watch their candidacy videos]]. When you are ready, go to the [[m:Special:SecurePoll/vote/405|SecurePoll voting page to vote]]. '''The vote is open from October 8 at 00:00 UTC to October 22 at 23:59 UTC.''' Best regards, Abhishek Suryawanshi<br />Chair, Elections Committee<section end="announcement-content" /> </div> [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 04:49, 9 Winterfylleð 2025 (UTC) <!-- Message sent by User:RamzyM (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29360896 --> == <span lang="en" dir="ltr">Help us decide the name of the new Abstract Wikipedia project</span> == <div lang="en" dir="ltr"> <section begin="function1"/> {{int:Hello}}. Please help pick a name for the new Abstract Wikipedia wiki project. This project will be a wiki that will enable users to combine functions from [[:f:|Wikifunctions]] and data from Wikidata in order to generate natural language sentences in any supported languages. These sentences can then be used by any Wikipedia (or elsewhere). There will be two rounds of voting, each followed by legal review of candidates, with votes beginning on 20 October and 17 November 2025. Our goal is to have a final project name selected on mid-December 2025. If you would like to participate, then '''[[m:Special:MyLanguage/Abstract Wikipedia/Abstract Wikipedia naming contest|please learn more and vote now]]''' at meta-wiki. {{Int:Feedback-thanks-title}} <section end="function1"/> </div> -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 11:44, 20 Winterfylleð 2025 (UTC) <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29432175 --> == <span lang="en" dir="ltr">Seeking volunteers to join several of the movement’s committees</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> Each year, typically from October through December, several of the movement’s committees seek new volunteers. Read more about the committees on their Meta-wiki pages: * [[m:Special:MyLanguage/Affiliations Committee|Affiliations Committee (AffCom)]] * [[m:Special:MyLanguage/Ombuds commission|Ombuds commission (OC)]] * [[m:Special:MyLanguage/Wikimedia Foundation/Legal/Community Resilience and Sustainability/Trust and Safety/Case Review Committee|Case Review Committee (CRC)]] Applications for the committees open on October 30, 2025. Applications for the Affiliations Committee, Ombuds commission and the Case Review Committee close on December 11, 2025. Learn how to apply by [[m:Special:MyLanguage/Wikimedia Foundation/Legal/Committee appointments|visiting the appointment page on Meta-wiki]]. Post to the talk page or email cst[[File:At sign.svg|16x16px|link=|(_AT_)]]wikimedia.org with any questions you may have. For the Committee Support team, <section end="announcement-content" /> </div> -[[m:User:MKaur (WMF)| MKaur (WMF)]] 14:13, 30 Winterfylleð 2025 (UTC) <!-- Message sent by User:MKaur (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29517125 --> == <span lang="en" dir="ltr">Reminder: Help us decide the name of the new Abstract Wikipedia project</span> == <div lang="en" dir="ltr"> <section begin="function2"/> {{int:Hello}}. Reminder: Please help to choose name for the new Abstract Wikipedia wiki project. The finalist vote starts today. The finalists for the name are: <span lang="en" dir="ltr" class="mw-content-ltr">Abstract Wikipedia, Multilingual Wikipedia, Wikiabstracts, Wikigenerator, Proto-Wiki</span>. If you would like to participate, then '''[[m:Special:MyLanguage/Abstract Wikipedia/Abstract Wikipedia naming contest|please learn more and vote now]]''' at meta-wiki. {{Int:Feedback-thanks-title}} <section end="function2"/> </div> -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 14:23, 20 Blotmonað 2025 (UTC) <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29583860 --> == Thank You for Last Year – Join Wiki Loves Ramadan 2026 == Dear Wikimedia communities, We hope you are doing well, and we wish you a happy New Year. ''Last year, we captured light. This year, we’ll capture legacy.'' In 2025, communities around the world shared the glow of Ramadan nights and the warmth of collective iftars. In 2026, ''Wiki Loves Ramadan'' is expanding, bringing more stories, more cultures, and deeper global connections across Wikimedia projects. We invite you to explore the ''Wiki Loves Ramadan 2026'' [[m:Special:MyLanguage/Wiki Loves Ramadan 2026|Meta page]] to learn how you can participate and [[m:Special:MyLanguage/Wiki Loves Ramadan 2026/Participating communities|sign up]] your community. 📷 ''Photo campaign on '' [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan 2026|Wikimedia Commons]] If you have questions about the project, please refer to the FAQs: * [[m:Special:MyLanguage/Wiki Loves Ramadan/FAQ/|Meta-Wiki]] * [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan/FAQ|Wikimedia Commons]] ''Early registration for updates is now open via the '''[[m:Special:RegisterForEvent/2710|Event page]]''''' ''Stay connected and receive updates:'' * [https://t.me/WikiLovesRamadan Telegram channel] * [https://lists.wikimedia.org/postorius/lists/wikilovesramadan.lists.wikimedia.org/ Mailing list] We look forward to collaborating with you and your community. '''The Wiki Loves Ramadan 2026 Organizing Team''' 19:45, 16 Se Æfterra Gēola 2026 (UTC) <!-- Message sent by User:ZI Jony@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29879549 --> == <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]]. The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]]. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" /> </div> 21:02, 19 Se Æfterra Gēola 2026 (UTC) <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 --> == Action Required: Update templates/modules for electoral maps (Migrating from P1846 to P14226) == Hello everyone, This is a notice regarding an ongoing data migration on Wikidata that may affect your election-related templates and Lua modules (such as <code>Module:Itemgroup/list</code>). '''The Change:'''<br /> Currently, many templates pull electoral maps from Wikidata using the property [[:d:Property:P1846|P1846]], combined with the qualifier [[:d:Property:P180|P180]]: [[:d:Q19571328|Q19571328]]. We are migrating this data (across roughly 4,000 items) to a newly created, dedicated property: '''[[:d:Property:P14226|P14226]]'''. '''What You Need To Do:'''<br /> To ensure your templates and infoboxes do not break or lose their maps, please update your local code to fetch data from [[:d:Property:P14226|P14226]] instead of the old [[:d:Property:P1846|P1846]] + [[:d:Property:P180|P180]] structure. A [[m:Wikidata/Property Migration: P1846 to P14226/List|list of pages]] was generated using Wikimedia Global Search. '''Deadline:'''<br /> We are temporarily retaining the old data on [[:d:Property:P1846|P1846]] to allow for a smooth transition. However, to complete the data cleanup on Wikidata, the old [[:d:Property:P1846|P1846]] statements will be removed after '''May 1, 2026'''. Please update your modules and templates before this date to prevent any disruption to your wiki's election articles. Let us know if you have any questions or need assistance with the query logic. Thank you for your help! [[User:ZI Jony|ZI Jony]] using [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 17:11, 3 Eastermonað 2026 (UTC) <!-- Message sent by User:ZI Jony@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29941252 --> == Request for comment (global AI policy) == <bdi lang="en" dir="ltr" class="mw-content-ltr"> Apologies for writing in English. {{int:Please-translate}} A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}} [[Brūcend:MediaWiki message delivery|MediaWiki message delivery]] ([[Brūcendmōtung:MediaWiki message delivery|motung]]) 00:58, 26 Eastermonað 2026 (UTC) </bdi> <!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 --> == <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> == <div lang="en" dir="ltr"> <section begin="announcement-content" /> Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC]. Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" /> </div> [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 Þrimilcemonað 2026 (UTC) <!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 --> == RFC about AI-generated content in Wikimedia Commons == <bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|motung]])</bdi> 17:12, 23 Sēremōnaþ 2026 (UTC) <!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 --> == <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> == <div lang="en" dir="ltr"> <section begin="Message"/> '''Legal & Safety Contacts''' Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]]. <section end="Message"/> </div> -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 Sēremōnaþ 2026 (UTC) <!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 --> == Request for comment (the future of Abstract Wikipedia) == <bdi lang="en" dir="ltr" class="mw-content-ltr"> Apologies if this has not yet been translated into your wiki's language. {{int:Please-translate}} You are invited to voice your opinions in a [[:m:Requests for comment/The future of Abstract Wikipedia|request for comment about the future of Abstract Wikipedia]]. {{Int:Feedback-thanks-title}} [[:m:User:Kowal2701|Kowal2701]] ([[:m:User talk:Kowal2701|talk]]) 12:25, 24 Mædmonað 2026 (UTC) </bdi> <!-- Message sent by User:DreamRimmer@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 --> == <span lang="en" dir="ltr">Server switch - Your wiki will be read-only for a short time soon</span> == <div lang="en" dir="ltr"> <section begin="server-switch"/><div class="plainlinks"> [[:m:Special:MyLanguage/Tech/Server switch|Read this message in another language]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}] The [[foundation:|Wikimedia Foundation]] will switch the traffic between its data centers. This will make sure that Wikipedia and the other Wikimedia wikis can stay online even after a disaster. All traffic will switch on '''{{#time:j xg|2026-09-23|en}}'''. The switch will start at '''[https://zonestamp.toolforge.org/{{#time:U|2026-09-23T14:00|en}} {{#time:H:i e|2026-09-23T14:00}}]'''. Unfortunately, because of some limitations in [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], all editing must stop while the switch is made. We apologize for this disruption, and we are working to minimize it in the future. A banner will be displayed on all wikis 30 minutes before this operation happens. This banner will remain visible until the end of the operation. You can contribute to the [https://meta.wikimedia.org/w/index.php?title=Special%3ATranslate&group=Centralnotice-tgroup-read_only_banner&task=view&language=&filter=&action=translate translation or proofreading] of this banner text. '''You will be able to read, but not edit, all wikis for a short period of time.''' * You will not be able to edit for up to an hour on {{#time:l j xg Y|2026-09-23|en}}. * If you try to edit or save during these times, you will see an error message. We hope that no edits will be lost during these minutes, but we can't guarantee it. If you see the error message, then please wait until everything is back to normal. Then you should be able to save your edit. But, we recommend that you make a copy of your changes first, just in case. ''Other effects'': * Background jobs will be slower and some may be dropped. Red links might not be updated as quickly as normal. If you create an article that is already linked somewhere else, the link will stay red longer than usual. Some long-running scripts will have to be stopped. * We expect the code deployments to happen as any other week. However, some case-by-case code freezes could punctually happen if the operation require them afterwards. * [[mw:Special:MyLanguage/GitLab|GitLab]] will be unavailable for about 90 minutes. This project may be postponed if necessary. You can [[wikitech:Switch_Datacenter|read the schedule at wikitech.wikimedia.org]]. Any changes will be announced in the schedule. '''Please share this information with your community.'''</div><section end="server-switch"/> </div> [[m:user:Trizek (WMF)|Trizek (WMF)]] 12:58, 15 Haligmonað 2026 (UTC) <!-- Message sent by User:Trizek (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=30513874 --> == Your feedback wanted: Remove ''(Language link added:) and similar'' edits from Special:RecentChanges == ''(Apologies for posting in English. You can help by translating it)'' Hello, I’m Danny from [[:en:w:Wikimedia_Deutschland|Wikimedia Deutschland’s]] [[m:Wikidata_For_Wikimedia_Projects|Wikidata For Wikimedia Projects]] team.<br/> We have been investigating and examining how Wikidata’s use in the [[Special:RecentChanges]] and [[Special:Watchlist]] pages can be made to be more useful, or less ‘noisy’ (fewer edits that have little value to the reader). We are currently exploring to make a change in the volume and the type of Wikidata edits that appear in these pages, and '''we need your feedback''' to help us ensure this is a positive change for the Wikis and won’t disrupt your workflows.<br/> Language links are also known as sitelinks or interlanguage links and describe when a Wikidata item is connected to a Wiki, adding it to the language dropdown tool for other connected Wikis. [[File:Types of Lang Link changes.png|x350px|frame|A variety of "Language Link" edits as seen in the Recent Changes feed]] The request for feedback is whether you support the removal of these language link edits from the Recent Changes and Watchlist feed, or would you oppose such a change and if so, why? Please share you thoughts on the dedicated '''[[m:Talk:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Hide_Language_Link_edits|metawiki Talk page]]'''. Further information about this change: [[m:Wikidata_For_Wikimedia_Projects/Clearer_Wikidata_Edit_Summaries/Hide_Language_Link_edits|m:Wikidata_For_Wikimedia_Projects/Hide_Language_Link_edits]] Thank you for any participation or feedback you give, -- [[m:User:Danny_Benjafield_(WMDE)|User:Danny Benjafield (WMDE)]] 12:48, 25 Haligmonað 2026 (UTC) <!-- Message sent by User:Danny Benjafield (WMDE)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_test_list&oldid=31081007 --> 7smyicwhayrercrkqfvl1cnh5a11mfz Dene 0 6387 54674 52261 2026-09-25T16:04:24Z Deadend0914 7211 /* Rihtstefn */ 54674 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== * IPA: /ˈde.ne/ === {{m}} {{Wordcynn|Nama}} === {{Naman-Tabule| Hwā oþþe hwæt? (Ānfeald)=- |Hwā oþþe hwæt? (Manigfeald)=þā Dene |Hwæs? (Ānfeald)=- |Hwæs? (Manigfeald)=þāra Dena |Hwǣm? (Ānfeald)=- |Hwǣm? (Manigfeald)=þǣm Denum |Hwȳ? (Ānfeald)=- |Hwȳ? (Manigfeald)=þǣm Denum |Hwone? (Ānfeald)=- |Hwone? (Manigfeald)=þā Dene }} #Þæt Denisce folc. [[Category:Mennisc]] [[Category:Englisc werlic nama]] [[Category:Þēod]] [[Category:Folc]] 0rznbymom1iule6nq6dz6uxmv7oz8tg eald 0 6391 54681 52295 2026-09-25T16:28:57Z Deadend0914 7211 /* {{Wordcynn|Tōgeīecendlic}} */ 54681 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== *[[w:SAMPA|SAMPA]]: /"{Ald/ *[[w:IPA|IPA]]: /ˈæɑld/ ===Wordstǣr=== === {{Wordcynn|Tōgeīecendlic}} === {{Tógeíecendlic-Tabule| 1. Stæpe=eald |2. Stæpe=ieldra |3. Stæpe=ieldest |Othera_declinunga=eald (declīnung) }} *tō bēonne gēbēon lange. ====Declīnung==== {{ang-adecl|eald}} [[Flocc:Englisc tōgeīecendlic]] 1k05ghfcbdwc20x5qkunsyoo5si1abf gōd 0 6392 54680 48750 2026-09-25T16:27:00Z Deadend0914 7211 /* Declīnung */ 54680 wikitext text/x-wiki ==Englisc== ===Āwendednesa=== * ===Rihtstefn=== *[[w:SAMPA|SAMPA]]: /go:d/ *[[w:IPA|IPA]]: /ɡoːd/ ===Andgiet=== * tōgeīecende # [[glēaw]] * bīword # wel ====wiþmetendlic==== * ====oferstīgendlic==== * ====Nīwenglisc Andgiet==== # good # well ===Declīnung=== {{ang-adecl|gōd}} ===Wordstǣr=== ====Gesibbword==== ====Gesetnesa==== ====Wīsan==== ====Sweostorword==== ====Bōccræft Fruma==== ==Wendunga== * === {{Wordcynn|Tōgeīecendlic}} === {{Tógeíecendlic-Tabule| 1. Stæpe=gōd |2. Stæpe=betera |3. Stæpe=betst |Othera_declinunga=gōd (declīnung) }} *tō wesanne mannþwære. [[Flocc:Englisc tōgeīecendas]] 41pbwcstk1h311bwxikbf5pd9effbh3 yfel 0 6393 54710 52303 2026-09-25T17:53:33Z Deadend0914 7211 54710 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== *[[w:SAMPA|SAMPA]]: /"yfel/, ["yvel] *[[w:IPA|IPA]]: /ˈyfel/, [ˈyvel] ===Wordstǣr=== === {{Wordcynn|Tōgeīecendlic}} === {{Tógeíecendlic-Tabule| 1. Stæpe=yfel |2. Stæpe=wyrsa |3. Stæpe=wyrst |Othera_declinunga=yfel (declīnung) }} *tō wesanne unriht. ===Declīnung=== {{ang-adecl|gedreóh}} [[Flocc:Englisc tōgeīecendlic]] 0foshlke8xpcmijspoea12kxiy6f3yy 54711 54710 2026-09-25T17:53:44Z Deadend0914 7211 /* Declīnung */ 54711 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== *[[w:SAMPA|SAMPA]]: /"yfel/, ["yvel] *[[w:IPA|IPA]]: /ˈyfel/, [ˈyvel] ===Wordstǣr=== === {{Wordcynn|Tōgeīecendlic}} === {{Tógeíecendlic-Tabule| 1. Stæpe=yfel |2. Stæpe=wyrsa |3. Stæpe=wyrst |Othera_declinunga=yfel (declīnung) }} *tō wesanne unriht. ===Declīnung=== {{ang-adecl|yfel}} [[Flocc:Englisc tōgeīecendlic]] 4rlthe1hau77a5ids6ctrxir1x9p9yp cyning 0 6412 54780 53901 2026-09-25T22:38:49Z Deadend0914 7211 /* Werlīc Nama */ 54780 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * cing, cicing, cyneg, cuning, cyng, cynig, cynincg, cyncg, cyngc ===Rihtstefn=== * /ˈkyninɡ/, [ˈkyniŋɡ] ===Wordstǣr=== Ealdorlic Germanisc: *[[Ætēaca:Ealdorlic Germanisc/kuningaz|kuningaz]] ====Sweostorword==== * Ealdnoren: [[konungr]] ** Denisc: [[konge]] * Ealdþēodisc: [[kuning]] ** Þēodisc: [[König]] * Niðerlendisc: [[koning]] ===Werlīc Nama=== {{ang-noun|wer|head=dēaþ}} # Werlīc wealdend landes oþþe folces. #: ''Se '''cyning''' wunode on þǣm castle.'' # Mihtig oþþe mǣre mann. #: ''On blindra lande biþ se ānēaga '''cyning'''. Swā ēac se ūþwita on þissum middangearde.'' ====Nīwenglisc Andgiet==== # [[king]], [[ruler]], [[leader]] # a powerful or important person ====Declīnung==== {| class="bluetable {{{1}}}" border="1" cellpadding="2" |- ! Cāsus ! Ānfeald ! Manigfeald |- | '''Nemniendlīc''' || se cyning || þā cyningas |- | '''Āgniendlīc''' || þæs cyninges || þāra cyninga |- | '''Forgifendlīc''' || þǣm cyninge || þǣm cyningum |- | '''Tōllīc''' || þȳ/þon cyninge || þǣm cyningum |- | '''Wrēgendlīc''' || þone cyning || þā cyningas |} ====Gesibbword==== cynn = kin <br> cyninge = queen <br> cynnig = noble, of noble family <br> ====Gesetnyssum==== þēod-cyning, [[folc]]-cyning - Folk-king <br> Angel-cyning - English king <br> Ānwintre-cyning, æwintrecyning - One winter king <br> [[æfter]]-cyning - later king <br> [[æðel]]-cyning - noble king (Christ) <br> bryten-cyning - powerful king <br> [[Ēaland]]-cyning - Island king <br> [[Gāst]]-cyning - Ghost king, soul king, God <br> [[Gūð]]-cyning - warrior king, <br> [[Hæðen]]-cyning - Heathen king <br> [[Hēah]]-cyning - High King, God <br> [[Heofon]]-cyning - King of Heaven <br> Lēod-cyning - King, rule, leader <br> [[Mægen]]-cyning - Mighty king <br> [[Riht]]-cyning - Lawful king <br> [[Sæ]]-cyning - Sea king <br> þrym-cyning - glorious king, god, king of glory, <br> [[woruld]]-cyning - earthly king, earth king <br> ====Bōccræft Fruman==== Willelm Cyng: stæfræwa 7, 13, <br> Aeþelþryþ: stfa. 2, 7, <br> Ohthere and Wulfstan: stfa. 1, 18, 23, 54, 61, <br> Judiþ: stfa. 155, 190, <br> Beowulf fōresægdnes: stfa. 2, 8, <br> Cynewulf and Cyneheard: stfa. 4, 5, 6, 7, 9, 10, 13, 14, 17, <br> ====Wendunga==== * Crēacisc: [[βασιλιάς]] * Ealdīrisc: [[rí]] * Ealdciricanslafisc: [[цѣсарь]] * Finnisc: [[kuningas]] * Frencisc: [[roi]] ** Ealdfrencisc: [[reis]] * Hebrēisc: [[מלך]] * Hittisc: [[lugal]] * Italisc: [[re]] * Lǣden: [[rex]] * Persisc: [[پادشاه]] * Polisc: [[król]] * Portuglisc: [[rei]] * Russisc: [[король]] * Spēonisc: [[rey]] [[Flocc:Englisc nama]] [[Flocc:Englisce lēode]] 5g1jncbjnhr1f4kqw95o6syeu1htzki 54781 54780 2026-09-25T22:42:06Z Deadend0914 7211 /* Werlīc Nama */ 54781 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * cing, cicing, cyneg, cuning, cyng, cynig, cynincg, cyncg, cyngc ===Rihtstefn=== * /ˈkyninɡ/, [ˈkyniŋɡ] ===Wordstǣr=== Ealdorlic Germanisc: *[[Ætēaca:Ealdorlic Germanisc/kuningaz|kuningaz]] ====Sweostorword==== * Ealdnoren: [[konungr]] ** Denisc: [[konge]] * Ealdþēodisc: [[kuning]] ** Þēodisc: [[König]] * Niðerlendisc: [[koning]] ===Werlīc Nama=== {{ang-noun|wer|head=cyning}} # Werlīc wealdend landes oþþe folces. #: ''Se '''cyning''' wunode on þǣm castle.'' # Mihtig oþþe mǣre mann. #: ''On blindra lande biþ se ānēaga '''cyning'''. Swā ēac se ūþwita on þissum middangearde.'' ====Nīwenglisc Andgiet==== # [[king]], [[ruler]], [[leader]] # a powerful or important person ====Declīnung==== {| class="bluetable {{{1}}}" border="1" cellpadding="2" |- ! Cāsus ! Ānfeald ! Manigfeald |- | '''Nemniendlīc''' || se cyning || þā cyningas |- | '''Āgniendlīc''' || þæs cyninges || þāra cyninga |- | '''Forgifendlīc''' || þǣm cyninge || þǣm cyningum |- | '''Tōllīc''' || þȳ/þon cyninge || þǣm cyningum |- | '''Wrēgendlīc''' || þone cyning || þā cyningas |} ====Gesibbword==== cynn = kin <br> cyninge = queen <br> cynnig = noble, of noble family <br> ====Gesetnyssum==== þēod-cyning, [[folc]]-cyning - Folk-king <br> Angel-cyning - English king <br> Ānwintre-cyning, æwintrecyning - One winter king <br> [[æfter]]-cyning - later king <br> [[æðel]]-cyning - noble king (Christ) <br> bryten-cyning - powerful king <br> [[Ēaland]]-cyning - Island king <br> [[Gāst]]-cyning - Ghost king, soul king, God <br> [[Gūð]]-cyning - warrior king, <br> [[Hæðen]]-cyning - Heathen king <br> [[Hēah]]-cyning - High King, God <br> [[Heofon]]-cyning - King of Heaven <br> Lēod-cyning - King, rule, leader <br> [[Mægen]]-cyning - Mighty king <br> [[Riht]]-cyning - Lawful king <br> [[Sæ]]-cyning - Sea king <br> þrym-cyning - glorious king, god, king of glory, <br> [[woruld]]-cyning - earthly king, earth king <br> ====Bōccræft Fruman==== Willelm Cyng: stæfræwa 7, 13, <br> Aeþelþryþ: stfa. 2, 7, <br> Ohthere and Wulfstan: stfa. 1, 18, 23, 54, 61, <br> Judiþ: stfa. 155, 190, <br> Beowulf fōresægdnes: stfa. 2, 8, <br> Cynewulf and Cyneheard: stfa. 4, 5, 6, 7, 9, 10, 13, 14, 17, <br> ====Wendunga==== * Crēacisc: [[βασιλιάς]] * Ealdīrisc: [[rí]] * Ealdciricanslafisc: [[цѣсарь]] * Finnisc: [[kuningas]] * Frencisc: [[roi]] ** Ealdfrencisc: [[reis]] * Hebrēisc: [[מלך]] * Hittisc: [[lugal]] * Italisc: [[re]] * Lǣden: [[rex]] * Persisc: [[پادشاه]] * Polisc: [[król]] * Portuglisc: [[rei]] * Russisc: [[король]] * Spēonisc: [[rey]] [[Flocc:Englisc nama]] [[Flocc:Englisce lēode]] lzxc2dk4nhd5wncn1b8vext6wfvgvm4 54782 54781 2026-09-25T22:43:12Z Deadend0914 7211 /* Declīnung */ 54782 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * cing, cicing, cyneg, cuning, cyng, cynig, cynincg, cyncg, cyngc ===Rihtstefn=== * /ˈkyninɡ/, [ˈkyniŋɡ] ===Wordstǣr=== Ealdorlic Germanisc: *[[Ætēaca:Ealdorlic Germanisc/kuningaz|kuningaz]] ====Sweostorword==== * Ealdnoren: [[konungr]] ** Denisc: [[konge]] * Ealdþēodisc: [[kuning]] ** Þēodisc: [[König]] * Niðerlendisc: [[koning]] ===Werlīc Nama=== {{ang-noun|wer|head=cyning}} # Werlīc wealdend landes oþþe folces. #: ''Se '''cyning''' wunode on þǣm castle.'' # Mihtig oþþe mǣre mann. #: ''On blindra lande biþ se ānēaga '''cyning'''. Swā ēac se ūþwita on þissum middangearde.'' ====Nīwenglisc Andgiet==== # [[king]], [[ruler]], [[leader]] # a powerful or important person ====Declīnung==== {{ang-decl-noun-a-m|cyning}} ====Gesibbword==== cynn = kin <br> cyninge = queen <br> cynnig = noble, of noble family <br> ====Gesetnyssum==== þēod-cyning, [[folc]]-cyning - Folk-king <br> Angel-cyning - English king <br> Ānwintre-cyning, æwintrecyning - One winter king <br> [[æfter]]-cyning - later king <br> [[æðel]]-cyning - noble king (Christ) <br> bryten-cyning - powerful king <br> [[Ēaland]]-cyning - Island king <br> [[Gāst]]-cyning - Ghost king, soul king, God <br> [[Gūð]]-cyning - warrior king, <br> [[Hæðen]]-cyning - Heathen king <br> [[Hēah]]-cyning - High King, God <br> [[Heofon]]-cyning - King of Heaven <br> Lēod-cyning - King, rule, leader <br> [[Mægen]]-cyning - Mighty king <br> [[Riht]]-cyning - Lawful king <br> [[Sæ]]-cyning - Sea king <br> þrym-cyning - glorious king, god, king of glory, <br> [[woruld]]-cyning - earthly king, earth king <br> ====Bōccræft Fruman==== Willelm Cyng: stæfræwa 7, 13, <br> Aeþelþryþ: stfa. 2, 7, <br> Ohthere and Wulfstan: stfa. 1, 18, 23, 54, 61, <br> Judiþ: stfa. 155, 190, <br> Beowulf fōresægdnes: stfa. 2, 8, <br> Cynewulf and Cyneheard: stfa. 4, 5, 6, 7, 9, 10, 13, 14, 17, <br> ====Wendunga==== * Crēacisc: [[βασιλιάς]] * Ealdīrisc: [[rí]] * Ealdciricanslafisc: [[цѣсарь]] * Finnisc: [[kuningas]] * Frencisc: [[roi]] ** Ealdfrencisc: [[reis]] * Hebrēisc: [[מלך]] * Hittisc: [[lugal]] * Italisc: [[re]] * Lǣden: [[rex]] * Persisc: [[پادشاه]] * Polisc: [[król]] * Portuglisc: [[rei]] * Russisc: [[король]] * Spēonisc: [[rey]] [[Flocc:Englisc nama]] [[Flocc:Englisce lēode]] hp0mjn13jm35ayruhlz5f4gtzy8k5ul dēaþ 0 6423 54670 53791 2026-09-25T15:36:12Z Deadend0914 7211 54670 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * deað * dǣþ ===Rihtstefn=== * /dæːɑθ/ ===Wordstǣr=== ====Sweostorword==== *Denisc: {{Wendung|da|død}} *[[Fresisc]]: {{Wendung|fy|dea}} ''wr'' *Īslendisc: {{Wendung|is|dauði}} ''wr'' *Niðerlendisc: {{Wendung|nl|dood}} ''wr'' *Norþwegisc: {{Wendung|no|død}} ''wr'' *[[Seaxisc]]: {{Wendung|nds|Dood}} ''wr'' *Swēonisc: {{Wendung|sv|död}} ''gm'' *Þēodisc: {{Wendung|de|Tod}} ''wr'' ===Werlīc Nama=== # Þæs līfes ende. #: '''''Dēaþ''' nis nān belimp on līfe. '''Dēaðes''' man ne gebītt. Gif man ēcnesse understent nā tō endelēasre tīdlengðe, ac tō tīdlēasnesse, þonne leofaþ sē on ēcnesse þe leofaþ on andweardnesse. Ūre līf is endelēas ealswā ūre gesihtfeld gemǣrelēas biþ.'' ====Nīwenglisc Andgiet==== # [[death]] ====Declīnung==== {| class="bluetable {{{1}}}" border="1" cellpadding="2" |- ! Cāsus ! Ānfeald ! Manigfeald |- | '''Nemniendlīc''' || se deaþ || þā deaþas |- | '''Āgniendlīc''' || þæs deaþes || þāra deaþa |- | '''Forgifendlīc''' || þǣm deaþe || þǣm deaþum |- | '''Tōllīc''' || þȳ/þon deaþe || þǣm deaþum |- | '''Wrēgendlīc''' || þone deaþ || þā deaþas |} ====Gesetnyssum==== [[ær]]-dēaþ - early death, death before one's time, premature death <br> dēaþ-[[bēam]] "wr" - death tree <br> dēaþ-[[bedd]] "nw" - death bed, grave, bed of death, <br> dēaþ-[[cwalu]] "wf" - plague death, violent death, <br> dēaþ-[[dæg]] "wr" - death day, day of death, <br> dēaþ-denu "wf" - valley of death, <br> fær-dēaþ, dēaþ-ræs "wr" - sudden death, <br> dēaþ-scūa "wr", dēaþ-scufa "wr" - death shadow, shadow of death, <br> dēaþ-[[scyld]] "wf" - crime worthy of death <br> dēaþ-stede "wr" - place of death <br> dēaþ-[[wang]] "wr" - death plain, <br> dēaþ-wīc "nw" - death's place, dwelling of the dead, <br> dēaþ-[[wyrd]] "wf" - death fate, fate of death <br> [[ende]]-dēað -death as the end of life <br> [[gúð]]-dēað - battle death, death in battle <br> [[mere]]-dēaþ - sea death, death at sea, <br> [[swylt]]-dēaþ - death (die-death) <br> [[wæl]]-dēaþ - death in battle, <br> [[wundor]]-dēaþ - wondrous death <br> ====Wendunga==== *Frencisc: {{Wendung|fr|mort}} ''wf'' *Grēcisc: {{Wendung|el|θάνατος}} [ˈθa.na.to̞s] ''wr'', {{Wendung|el|θανατάς}} [θa.na.ˈtas] ''wr'', {{Wendung|el|πεθαμός}} [pe̞.θa.ˈmo̞s] ''wr'', {{Wendung|el|αποθαμός}} [a.po̞.θa.ˈmo̞s] {{m}}, {{Wendung|el|χάρος}} [ˈxa.ro̞s] ''wr'' *Hebrēisc: {{Wendung|he|מוות}} (mavet) ''wf'' *Italisc: {{Wendung|it|morte}} ''wf'' *Lǣden: {{Wendung|la|mors}} ''wf'', {{Wendung|la|exitium}} ''nā'', {{Wendung|la|quietus}} ''wr'' *Persisc: {{Wendung|fa|مرگ}} *Scyttisc Gǣlisc: {{Wendung|gd|bàs}} ''wr'' [[Flocc:Englisc nama]] [[Flocc: Englisc werlīc nama]] m5rnokvgvzmxuscen7fhuw4nnn9fw6t 54722 54670 2026-09-25T20:55:57Z Deadend0914 7211 54722 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * deað * dǣþ ===Rihtstefn=== * /dæːɑθ/ ===Wordstǣr=== ====Sweostorword==== *Denisc: {{Wendung|da|død}} *[[Fresisc]]: {{Wendung|fy|dea}} ''wr'' *Īslendisc: {{Wendung|is|dauði}} ''wr'' *Niðerlendisc: {{Wendung|nl|dood}} ''wr'' *Norþwegisc: {{Wendung|no|død}} ''wr'' *[[Seaxisc]]: {{Wendung|nds|Dood}} ''wr'' *Swēonisc: {{Wendung|sv|död}} ''gm'' *Þēodisc: {{Wendung|de|Tod}} ''wr'' ===Werlīc Nama=== # Þæs līfes ende. #: '''''Dēaþ''' nis nān belimp on līfe. '''Dēaðes''' man ne gebītt. Gif man ēcnesse understent nā tō endelēasre tīdlengðe, ac tō tīdlēasnesse, þonne leofaþ sē on ēcnesse þe leofaþ on andweardnesse. Ūre līf is endelēas ealswā ūre gesihtfeld gemǣrelēas biþ.'' ====Nīwenglisc Andgiet==== # [[death]] ====Declīnung==== {{ang-decl-noun-a-m|dēaþ}} ====Gesetnyssum==== [[ær]]-dēaþ - early death, death before one's time, premature death <br> dēaþ-[[bēam]] "wr" - death tree <br> dēaþ-[[bedd]] "nw" - death bed, grave, bed of death, <br> dēaþ-[[cwalu]] "wf" - plague death, violent death, <br> dēaþ-[[dæg]] "wr" - death day, day of death, <br> dēaþ-denu "wf" - valley of death, <br> fær-dēaþ, dēaþ-ræs "wr" - sudden death, <br> dēaþ-scūa "wr", dēaþ-scufa "wr" - death shadow, shadow of death, <br> dēaþ-[[scyld]] "wf" - crime worthy of death <br> dēaþ-stede "wr" - place of death <br> dēaþ-[[wang]] "wr" - death plain, <br> dēaþ-wīc "nw" - death's place, dwelling of the dead, <br> dēaþ-[[wyrd]] "wf" - death fate, fate of death <br> [[ende]]-dēað -death as the end of life <br> [[gúð]]-dēað - battle death, death in battle <br> [[mere]]-dēaþ - sea death, death at sea, <br> [[swylt]]-dēaþ - death (die-death) <br> [[wæl]]-dēaþ - death in battle, <br> [[wundor]]-dēaþ - wondrous death <br> ====Wendunga==== *Frencisc: {{Wendung|fr|mort}} ''wf'' *Grēcisc: {{Wendung|el|θάνατος}} [ˈθa.na.to̞s] ''wr'', {{Wendung|el|θανατάς}} [θa.na.ˈtas] ''wr'', {{Wendung|el|πεθαμός}} [pe̞.θa.ˈmo̞s] ''wr'', {{Wendung|el|αποθαμός}} [a.po̞.θa.ˈmo̞s] {{m}}, {{Wendung|el|χάρος}} [ˈxa.ro̞s] ''wr'' *Hebrēisc: {{Wendung|he|מוות}} (mavet) ''wf'' *Italisc: {{Wendung|it|morte}} ''wf'' *Lǣden: {{Wendung|la|mors}} ''wf'', {{Wendung|la|exitium}} ''nā'', {{Wendung|la|quietus}} ''wr'' *Persisc: {{Wendung|fa|مرگ}} *Scyttisc Gǣlisc: {{Wendung|gd|bàs}} ''wr'' [[Flocc:Englisc nama]] [[Flocc: Englisc werlīc nama]] kskuetjtapk1cbk1k80ep9debfad3js 54727 54722 2026-09-25T21:04:22Z Deadend0914 7211 /* Declīnung */ 54727 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * deað * dǣþ ===Rihtstefn=== * /dæːɑθ/ ===Wordstǣr=== ====Sweostorword==== *Denisc: {{Wendung|da|død}} *[[Fresisc]]: {{Wendung|fy|dea}} ''wr'' *Īslendisc: {{Wendung|is|dauði}} ''wr'' *Niðerlendisc: {{Wendung|nl|dood}} ''wr'' *Norþwegisc: {{Wendung|no|død}} ''wr'' *[[Seaxisc]]: {{Wendung|nds|Dood}} ''wr'' *Swēonisc: {{Wendung|sv|död}} ''gm'' *Þēodisc: {{Wendung|de|Tod}} ''wr'' ===Werlīc Nama=== # Þæs līfes ende. #: '''''Dēaþ''' nis nān belimp on līfe. '''Dēaðes''' man ne gebītt. Gif man ēcnesse understent nā tō endelēasre tīdlengðe, ac tō tīdlēasnesse, þonne leofaþ sē on ēcnesse þe leofaþ on andweardnesse. Ūre līf is endelēas ealswā ūre gesihtfeld gemǣrelēas biþ.'' ====Nīwenglisc Andgiet==== # [[death]] ====Declīnung==== {{ang-decl-noun-a-m|dēaþ}} ====Gesetnyssum==== [[ær]]-dēaþ - early death, death before one's time, premature death <br> dēaþ-[[bēam]] "wr" - death tree <br> dēaþ-[[bedd]] "nw" - death bed, grave, bed of death, <br> dēaþ-[[cwalu]] "wf" - plague death, violent death, <br> dēaþ-[[dæg]] "wr" - death day, day of death, <br> dēaþ-denu "wf" - valley of death, <br> fær-dēaþ, dēaþ-ræs "wr" - sudden death, <br> dēaþ-scūa "wr", dēaþ-scufa "wr" - death shadow, shadow of death, <br> dēaþ-[[scyld]] "wf" - crime worthy of death <br> dēaþ-stede "wr" - place of death <br> dēaþ-[[wang]] "wr" - death plain, <br> dēaþ-wīc "nw" - death's place, dwelling of the dead, <br> dēaþ-[[wyrd]] "wf" - death fate, fate of death <br> [[ende]]-dēað -death as the end of life <br> [[gúð]]-dēað - battle death, death in battle <br> [[mere]]-dēaþ - sea death, death at sea, <br> [[swylt]]-dēaþ - death (die-death) <br> [[wæl]]-dēaþ - death in battle, <br> [[wundor]]-dēaþ - wondrous death <br> ====Wendunga==== *Frencisc: {{Wendung|fr|mort}} ''wf'' *Grēcisc: {{Wendung|el|θάνατος}} [ˈθa.na.to̞s] ''wr'', {{Wendung|el|θανατάς}} [θa.na.ˈtas] ''wr'', {{Wendung|el|πεθαμός}} [pe̞.θa.ˈmo̞s] ''wr'', {{Wendung|el|αποθαμός}} [a.po̞.θa.ˈmo̞s] {{m}}, {{Wendung|el|χάρος}} [ˈxa.ro̞s] ''wr'' *Hebrēisc: {{Wendung|he|מוות}} (mavet) ''wf'' *Italisc: {{Wendung|it|morte}} ''wf'' *Lǣden: {{Wendung|la|mors}} ''wf'', {{Wendung|la|exitium}} ''nā'', {{Wendung|la|quietus}} ''wr'' *Persisc: {{Wendung|fa|مرگ}} *Scyttisc Gǣlisc: {{Wendung|gd|bàs}} ''wr'' [[Flocc:Englisc nama]] [[Flocc: Englisc werlīc nama]] 3lc6edwjeeqomm6y0o987yegaf0di2m cild 0 6428 54784 53232 2026-09-25T22:46:21Z Deadend0914 7211 /* Declinung */ 54784 wikitext text/x-wiki =={{sprǣc|ang}}== [[File:ENFANTS-BLONDS.jpg|thumb|'''cild''']] ===Rihtstefn=== * /tʃild/ ===Wordstǣr=== Ealdorlic Germanisc: *[[Ætēaca:Ealdorlic Germanisc/kelþą|kelþą]] ===Nāhwæðer Nama=== 1: [[bearn]] "nw" <br> 2: [[cnafa]] "wr" :[[cnapa]] "wr" :[[lȳtling]] "wr" :[[magutimber]] "nw" 3: [[sunu]] oþþe [[dōhtor]] ====Bisen==== Se '''Cyning''' nǣfde næfre cild. : The king never had children. ====Nīwenglisc Andgiet==== 1: [[child]] : infant : youth ====Declinung==== {{ang-decl-noun-z-n|cild}} ====Gesibbword==== cildlic - youth <br> cildisc - childish <br> cildhād - childhood <br> cildfaru - child baring <br> cildsung - childishness <br> ====Gesetnyssum==== [[cniht]]-cild - youth, male child, <br> cradol-cild - cradle child, child in the cradle <br> hyse-cild, wæpned-cild - male child <br> [[mæden]]-cild, wīf-cild - female child <br> [[mōdor]]-cild - child of one's mother <br> munuc-cild - "monk-child" monastic child <br> cild-[[geong]] - "adj" child-youth, young <br> ====Wendunga==== * Denisc: [[barn]] * Ealdnoren: [[barn]] * Frencisc: [[enfant]] * Hebrēisc: [[ילד]] * Italisc: [[bambino]] * Lǣden: [[infans]] * Noren: [[barn]] * Niðerlendisc: [[kind]] * Persisc: [[کودک]] * Spēonisc: [[niño]] * Þēodisc: [[Kind]] [[Flocc:Englisc nama]] [[Flocc:Englisce lēode]] [[Flocc:Englisc mǣgþ]] f5fxpqdnky6cq5pxpfd7q3jlkfbnc1c Bysen:sprǣc 10 7569 54669 54667 2026-09-25T15:32:37Z Deadend0914 7211 54669 wikitext text/x-wiki {{#switch:{{{1}}} | ang = Englisc | af = Affricanisc | bn = Bengalisc | ca = Catalānisc | cs = Bæmisc | da = Denisc | de = Þēodisc | el = Crēacisc | en = Níwe Englisc | en = Nīwenglisc | eo = Esperantisc | es = Spēonisc | et = Eotolisc | eu = Wascisc | fa = Persisc | fi = Finnisc | fo = Faerisc | fr = Frencisc | frp = Profencalisc | ga = Īrisc | gl = Galizisc | gn = Awaisc | goh = Ealdhēahþēodisc | gv = Manisc | got = Gotisc | haw = Hawaiisc | hi = Hindisc | hu = Hungarisc | id = Indonesisc | io = Idoisc | is = Īslendisc | it = Italisc | iu = Inuctitutisc | la = Lǣden | li = Limburgisc | lt = Litwisc | lv = Lettisc | mul = Þēodeorþe | nap = Neapolitisc | nl = Niðerlendisc | no = Norþwegisc | non = Eald Noren | ovd = Ælfdælisc | pl = Polisc | pt = Portugisc | qu = Kescwisc | ro = Rumǣnisc | sa = Sanscritisc | sco = Niderscyttisc | sq = Albānisc | sv = Swēonisc | tpi = Tok Pisin | vo = Volapük | ? }} d8c0fmiida8q1boznlw1afmoxpr8nqa 54789 54669 2026-09-26T01:43:27Z Deadend0914 7211 54789 wikitext text/x-wiki {{#switch:{{{1}}} | ang = Englisc | af = Affricanisc | bn = Bengalisc | ca = Catalānisc | cs = Bæmisc | da = Denisc | de = Þēodisc | el = Crēacisc | en = Nīwenglisc | eo = Esperantisc | es = Spēonisc | et = Eotolisc | eu = Wascisc | fa = Persisc | fi = Finnisc | fo = Faerisc | fr = Frencisc | frp = Profencalisc | ga = Īrisc | gl = Galizisc | gn = Awaisc | goh = Ealdhēahþēodisc | gv = Manisc | got = Gotisc | haw = Hawaiisc | hi = Hindisc | hu = Hungarisc | id = Indonesisc | io = Idoisc | is = Īslendisc | it = Italisc | iu = Inuctitutisc | la = Lǣden | li = Limburgisc | lt = Litwisc | lv = Lettisc | mul = Þēodeorþe | nap = Neapolitisc | nl = Niðerlendisc | no = Norþwegisc | non = Eald Noren | ovd = Ælfdælisc | pl = Polisc | pt = Portugisc | qu = Kescwisc | ro = Rumǣnisc | sa = Sanscritisc | sco = Niderscyttisc | sq = Albānisc | sv = Swēonisc | tpi = Tok Pisin | vo = Volapük | ? }} o7ym9d9equq0qxddah3rdnmqco3kuu7 Module:table 828 7861 54738 54379 2026-09-25T21:30:14Z Deadend0914 7211 54738 Scribunto text/plain local export = {} --[==[ intro: This module provides functions for dealing with Lua tables. All of them, except for two helper functions, take a table as their first argument. Some functions are available as methods in the arrays created by [[Module:array]]. Functions by what they do: * Create a new table: ** `shallowCopy`, `deepCopy`, `removeDuplicates`, `numKeys`, `compressSparseArray`, `keysToList`, `reverse`, `invert`, `listToSet` * Create an array: ** `removeDuplicates`, `numKeys`, `compressSparseArray`, `keysToList`, `reverse` * Return information about the table: ** `size`, `length`, `contains`, `isArray`, `deepEquals` * Treat the table as an array (that is, operate on the values in the array portion of the table: values indexed by consecutive integers starting at {1}): ** `removeDuplicates`, `length`, `contains`, `serialCommaJoin`, `reverseIpairs`, `reverse`, `invert`, `listToSet`, `isArray` * Treat a table as a sparse array (that is, operate on values indexed by non-consecutive integers): ** `numKeys`, `maxIndex`, `compressSparseArray`, `sparseConcat`, `sparseIpairs` * Generate an iterator: ** `sparseIpairs`, `sortedPairs`, `reverseIpairs` * Other functions: ** `sparseConcat`, `serialCommaJoin`, `reverseConcat` The original version was a copy of {{w|Module:TableTools}} on Wikipedia via [[c:Module:TableTools|Module:TableTools]] on Commons, but in the course of time this module has been almost completely rewritten, with many new functions added. The main legacy of this is the use of camelCase for function names rather than snake_case, as is normal in the English Wiktionary. ]==] local load_module = "Module:load" local math_module = "Module:math" local table = table local concat = table.concat local dump = mw.dumpObject local ipairs = ipairs local ipairs_default_iter = ipairs{export} local next = next local pairs = pairs local require = require local select = select local signed_index -- defined as export.signedIndex local table_len -- defined as export.length local type = type --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function safe_require(...) safe_require = require(load_module).safe_require return safe_require(...) end --[==[ Given an array and a signed index, returns the true table index. If the signed index is negative, the array will be counted from the end, where {-1} is the highest index in the array; otherwise, the returned index will be the same. To aid optimization, the first argument may be a number representing the array length instead of the array itself; this is useful when the array length is already known, as it avoids recalculating it each time this function is called.]==] function export.signedIndex(t, k) if not is_integer(k) then error("index must be an integer") end return k < 0 and (type(t) == "table" and table_len(t) or t) + k + 1 or k end signed_index = export.signedIndex --[==[ An iterator which works like `pairs`, but ignores any `__pairs` metamethod.]==] function export.rawPairs(t) return next, t, nil end --[==[ An iterator which works like `ipairs`, but ignores any `__ipairs` metamethod.]==] function export.rawIpairs(t) return ipairs_default_iter, t, 0 end --[==[ This returns the length of a table, or the first integer key n counting from 1 such that t[n + 1] is nil. It is a more reliable form of the operator `#`, which can become unpredictable under certain circumstances due to the implementation of tables under the hood in Lua, and therefore should not be used when dealing with arbitrary tables. `#` also does not use metamethods, so will return the wrong value in cases where it is desirable to take these into account (e.g. data loaded via `mw.loadData`). If `raw` is set, then metamethods will be ignored, giving the true table length. For arrays, this function is faster than `export.size`.]==] function export.length(t, raw) local n = 0 if raw then for i in ipairs_default_iter, t, 0 do n = i end return n end repeat n = n + 1 until t[n] == nil return n - 1 end table_len = export.length local function getIteratorValues(i, j , step, t_len) i, j = i and signed_index(t_len, i), j and signed_index(t_len, j) if step == nil then i, j = i or 1, j or t_len return i, j, j < i and -1 or 1 elseif step == 0 or not is_integer(step) then error("step must be a non-zero integer") elseif step < 0 then return i or t_len, j or 1, step end return i or 1, j or t_len, step end --[==[ Given an array `list` and function `func`, iterate through the array applying {func(r, k, v)}, and returning the result, where `r` is the value calculated so far, `k` is an index, and `v` is the value at index `k`. For example, {reduce(array, function(a, _, v) return a + v end)} will return the sum of `array`. Optional arguments: * `i`: start index; negative values count from the end of the array * `j`: end index; negative values count from the end of the array * `step`: step increment These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or backwards and by how much, based on these inputs (see examples below for default behaviours). Examples: # No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default). # step=-1 results in backward iteration from the end to the start in steps of 1. # i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1). # j=-3 results in forward iteration from the start to the 3rd last index. # j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==] function export.reduce(t, func, i, j, step) i, j, step = getIteratorValues(i, j, step, table_len(t)) local ret = t[i] for k = i + step, j, step do ret = func(ret, k, t[k]) end return ret end do local function replace(t, func, i, j, step, generate) local t_len = table_len(t) -- Normalized i, j and step, based on the inputs. local norm_i, norm_j, norm_step = getIteratorValues(i, j, step, t_len) if norm_step > 0 then i, j, step = 1, t_len, 1 else i, j, step = t_len, 1, -1 end -- "Signed" variables are multiplied by -1 if `step` is negative. local t_new, signed_i, signed_j = generate and {} or t, norm_i * step, norm_j * step for k = i, j, step do -- Replace the values iff they're within the i to j range and `step` wouldn't skip the key. -- Note: i > j if `step` is positive; i < j if `step` is negative. Otherwise, the range is empty. local signed_k = k * step if signed_k >= signed_i and signed_k <= signed_j and (k - norm_i) % norm_step == 0 then t_new[k] = func(k, t[k]) -- Otherwise, add the existing value if `generate` is set. elseif generate then t_new[k] = t[k] end end return t_new end --[==[ Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and `v` is the value at index `k`), replacing the relevant values with the result. For example, {apply(array, function(_, v) return 2 * v end)} will double each member of the array. Optional arguments: * `i`: start index; negative values count from the end of the array * `j`: end index; negative values count from the end of the array * `step`: step increment These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or backwards and by how much, based on these inputs (see examples below for default behaviours). Examples: # No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default). # step=-1 results in backward iteration from the end to the start in steps of 1. # i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1). # j=-3 results in forward iteration from the start to the 3rd last index. # j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==] function export.apply(t, func, i, j, step) return replace(t, func, i, j, step, false) end --[==[ Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and `v` is the value at index `k`), and return a shallow copy of the original array with the relevant values replaced. For example, {generate(array, function(_, v) return 2 * v end)} will return a new array in which each value has been doubled. Optional arguments: * `i`: start index; negative values count from the end of the array * `j`: end index; negative values count from the end of the array * `step`: step increment These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or backwards and by how much, based on these inputs (see examples below for default behaviours). Examples: # No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default). # step=-1 results in backward iteration from the end to the start in steps of 1. # i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1). # j=-3 results in forward iteration from the start to the 3rd last index. # j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==] function export.generate(t, func, i, j, step) return replace(t, func, i, j, step, true) end end --[==[ Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and `v` is the value at index `k`), and returning whether the function is true for all iterations. Optional arguments: * `i`: start index; negative values count from the end of the array * `j`: end index; negative values count from the end of the array * `step`: step increment These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or backwards and by how much, based on these inputs (see examples below for default behaviours). Examples: # No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default). # step=-1 results in backward iteration from the end to the start in steps of 1. # i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1). # j=-3 results in forward iteration from the start to the 3rd last index. # j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==] function export.all(t, func, i, j, step) i, j, step = getIteratorValues(i, j, step, table_len(t)) for k = i, j, step do if not func(k, t[k]) then return false end end return true end --[==[ Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and `v` is the value at index `k`), and returning whether the function is true for at least one iteration. Optional arguments: * `i`: start index; negative values count from the end of the array * `j`: end index; negative values count from the end of the array * `step`: step increment These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or backwards and by how much, based on these inputs (see examples below for default behaviours). Examples: # No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default). # step=-1 results in backward iteration from the end to the start in steps of 1. # i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1). # j=-3 results in forward iteration from the start to the 3rd last index. # j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==] function export.any(t, func, i, j, step) i, j, step = getIteratorValues(i, j, step, table_len(t)) for k = i, j, step do if not not (func(k, t[k])) then return true end end return false end --[==[ Joins an array with serial comma and serial conjunction, normally {"and"}. An improvement on {mw.text.listToText}, which doesn't properly handle serial commas. Options: * `conj`: Conjunction to use; defaults to {"and"}. * `punc`: Punctuation to use; default to {","}. * `dontTag`: Don't tag the serial comma and serial {"and"}. For error messages, in which HTML cannot be used. * `dump`: Each item will be serialized with {mw.dumpObject}. For warnings and error messages.]==] function export.serialCommaJoin(seq, options) -- If the `dump` option is set, determine the table length as part of the -- dump loop, instead of calling `table_len` separately. local length if options and options.dump then local i, item = 1, seq[1] if item ~= nil then local dumped = {} repeat dumped[i] = dump(item) i = i + 1 item = seq[i] until item == nil seq = dumped end length = i - 1 else length = table_len(seq) end if length == 0 then return "" elseif length == 1 then return seq[1] end local conj = options and options.conj if conj == nil then conj = "and" end if length == 2 then return seq[1] .. " " .. conj .. " " .. seq[2] end local punc, dont_tag if options then punc = options.punc if punc == nil then punc = "," end dont_tag = options.dontTag else punc = "," end local comma if dont_tag then comma = "" -- since by default the serial comma doesn't display, when we can't tag we shouldn't display it. conj = " " .. conj .. " " else comma = "<span class=\"serial-comma\">" .. punc .. "</span>" conj = "<span class=\"serial-and\"> " .. conj .. "</span> " end return concat(seq, punc .. " ", 1, length - 1) .. comma .. conj .. seq[length] end --[==[ A function which works like `table.concat`, but respects any `__index` metamethod. This is useful for data loaded via `mw.loadData`.]==] function export.concat(t, sep, i, j) local list, k = {}, 0 while true do k = k + 1 local v = t[k] if v == nil then return concat(list, sep, i, j) end list[k] = v end end --[==[ Add a list of aliases for a given key to a table. The aliases must be given as a table.]==] function export.alias(t, k, aliases) for _, alias in pairs(aliases) do t[alias] = t[k] end end local mt = {} function mt:__index(k) local submodule = safe_require("Module:table/" .. k) self[k] = submodule return submodule end return setmetatable(export, mt) exw27st65r96fvgc4krew9cy4eyvdow Module:utilities 828 7864 54730 54382 2026-09-25T21:08:31Z Deadend0914 7211 54730 Scribunto text/plain local export = {} local headword_data_module = "Module:headword/data" local languages_module = "Module:languages" local links_module = "Module:links" local load_module = "Module:load" local pages_module = "Module:pages" local script_utilities_module = "Module:script utilities" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local utilities_data_module = "Module:utilities/data" local mw = mw local anchor_encode = mw.uri.anchorEncode local concat = table.concat local format_categories -- Defined below. local ipairs = ipairs local require = require local type = type local unstrip = mw.text.unstrip --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function is_content_page(...) is_content_page = require(pages_module).is_content_page return is_content_page(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local catfix_scripts local function get_catfix_scripts() catfix_scripts, get_catfix_scripts = load_data(utilities_data_module).catfix_scripts, nil return catfix_scripts end local current_title local function get_current_title() current_title, get_current_title = mw.title.getCurrentTitle(), nil return current_title end local defaultsort local function get_defaultsort() defaultsort, get_defaultsort = load_data(headword_data_module).page.pagename_defaultsort, nil return defaultsort end local pagename local function get_pagename() pagename, get_pagename = load_data(headword_data_module).page.encoded_pagename, nil return pagename end local und local function get_und() und, get_und = require(languages_module).getByCode("und"), nil return und end do local function handle_url(capture) return capture:match("https?://[^%s%]]+%s([^%]]+)") or "" end --[==[ A helper function to strip wiki markup, giving the plaintext of what is displayed on the page.]==] function export.get_plaintext(text) text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- Remove strip markers and HTML tags. text = unstrip(text):gsub("<[^<>\1\2]+>", "") -- Parse internal links for the display text, and remove categories. text = remove_links(text) -- Remove files. text = text:gsub("\1[Ff][Ii][Ll][Ee]:[^\1\2]+\2", "") :gsub("\1[Ii][Mm][Aa][Gg][Ee]:[^\1\2]+\2", "") -- Parse external links for the display text. text = text:gsub("%[(https?://[^%[%]]+)%]", handle_url) -- Any remaining square brackets aren't involved in links, but must be escaped to avoid creating new links. :gsub("\1", "&#91;&#91;") :gsub("\2", "&#93;&#93;") :gsub("%[", "&#91;") :gsub("]", "&#93;") -- Strip bold, italics and soft hyphens. :gsub("('*)'''(.-'*)'''", "%1%2") :gsub("('*)''(.-'*)''", "%1%2") :gsub("­", "") -- Get any HTML entities and trim. -- Note: don't decode URL percent encoding, as it shouldn't be used in display text and may cause problems if % is used. return trim(decode_entities(text)) end end do local function generate_sortkey(lang, sc, sort_key, sort_base) -- If the sort key is "-", treat the language as undetermined (the default). This is desirable when categorising (e.g.) translation requests, as the pages to be categorised are always in English/Translingual. if sort_key == "-" then lang, sort_key = und or get_und(), nil elseif not lang then lang = und or get_und() end -- Generate the automatic sort key. local auto = lang:makeSortKey(sort_base or pagename or get_pagename(), sc) -- Use the page defaultsort if necessary. Try hard to avoid empty sort key, which causes categories to be -- displayed as raw text; fall back to pagename if necessary. if not auto or auto == "" then auto = defaultsort or get_defaultsort() end if not auto or auto == "" then auto = sort_base end if not auto or auto == "" then auto = pagename or get_pagename() end -- If not sort key specified, use the automatic one. if not sort_key or sort_key == "" then return auto -- Otherwise, if the language is not "und", categorize the manual sort key as either redundant or non-redundant. -- FIXME: we should do this for "und" as well, but "Undetermined terms..." does not make sense for translations etc. elseif lang:getCode() ~= "und" then return sort_key, "[[Category:" .. lang:getFullName() .. " terms with " .. ( uupper(sort_key) == auto and "redundant" or "non-redundant non-automated" ) .. " sortkeys|" .. sort_key .. "]]" end return sort_key end --[==[ Format the categories with the appropriate sort key. * `cat` can take two forms: ** A string (the full category, minus the {"Category:"} prefix); ** A list of categories. Each category in the list can be either: *** A string in the same format as above; *** An object with the fields: **** `cat`: a string in the same format as above (required); **** `lang`: an optional language object to override the overall `lang`; **** `sc`: an optional script object to override the overall `sc`. **** `sort_key`: an optional sort key to override the overall `sort_key`; **** `sort_base`: an optional sort base to override the overall `sort_base`; * `lang` is an object encapsulating a language; if {nil}, the object for language code {"und"} (undetermined) will be used. `lang` is used when computing the sort key (either from the subpage name or sort base). * `sort_key` is placed in the category invocation, and indicates how the page will sort in the respective category. Normally '''do not use this'''. Instead, leave it {nil}, and if you need to a control the sort order, use {sort_base}, so that language-specific normalization is applied on top of the specified sort base. If neither {sort_key} nor {sort_base} is specified, the default is to apply language-specific normalization to the subpage name; see below. * `sort_base` lets you override the default sort key while still maintaining appropriate language-specific normalization. If {nil} is specified, this defaults to the subpage name, which is the portion of the full pagename after subtracting the namespace prefix (and, in certain namespaces such as {User:}, but notably not in the mainspace, after subtracting anything up through the final slash). The actual sort key is derived from the sort base approximately by lowercasing, applying language-specific normalization and then uppercasing; note that the same process is applied in deriving the sort key when no sort base is specified. For example, for French, Spanish, etc. the normalization process maps accented letters to their unaccented equivalents, so that e.g. in French, {{m|fr|ça}} sorts after {{m|fr|ca}} (instead of after the default Wikimedia sort order, which is approximately based on Unicode sort order and places ç after z) and {{m|fr|côté}} sorts after {{m|fr|coté}} (instead of between c and d). Similarly, in Russian the normalization process converts Cyrillic ё to a string consisting of Cyrillic е followed by U+10FFFF, so that effectively ё sorts after е instead of the default Wikimedia sort, which (I think) puts ё after я, the last letter of the Cyrillic alphabet. * `force_output` forces normal output in all namespaces. Normally, nothing is output if the page isn't in the main, Appendix:, Thesaurus:, Reconstruction: or Citations: namespaces. * `sc` is a script object; if nil, the default will be derived from the sort base (or its default value, the subpage name) by calling {lang:findBestScript()}. The value of `sc` is used during the sort base normalization process; for example, languages with multiple scripts will often have script-specific normalization processes.]==] function export.format_categories(cat, lang, sort_key, sort_base, force_output, sc) if not ( force_output or is_content_page(current_title or get_current_title()) or current_title.prefixedText == "Wiktionary:Sandbox" ) then return "" elseif type(cat) == "string" then local this_sort_key, extra = generate_sortkey(lang, sc, sort_key, sort_base) return "[[Category:" .. cat .. "|" .. this_sort_key .. "]]" .. (extra or "") end local ret, i, n, default = {}, 0, 0 -- Iterate over all categories in `cat`. while true do i = i + 1 local category = cat[i] if category == nil then return concat(ret) end local this_sort_key, extra -- If the category type is a table, use any custom options in it. if type(category) == "table" then category, this_sort_key, extra = category.cat, generate_sortkey( category.lang or lang, category.sc or sc, category.sort_key or sort_key, category.sort_base or sort_base ) -- If `default` has already been determined, use it. elseif default then this_sort_key = default -- Otherwise, calculate `default` and use it. else this_sort_key, extra = generate_sortkey(lang, sc, sort_key, sort_base) default = this_sort_key end n = n + 1 ret[n] = "[[Category:" .. category .. "|" .. this_sort_key .. "]]" .. (extra or "") end end format_categories = export.format_categories end --[==[ Add a "catfix", which is used on language-specific category pages to add language attributes and often script classes to all entry names. The addition of language attributes and script classes makes the entry names display better (using the language- or script-specific styles specified in [[MediaWiki:Common.css]]), which is particularly important for non-English languages that do not have consistent font support in browsers. Language attributes are added for all languages, but script classes are only added for languages with one script listed in their data file, or for languages that have a default script listed in the {catfix_script} list in [[Module:utilities/data]]. Some languages clearly have a default script, but still have other scripts listed in their data file. If those other scripts are not simply scripts like {{cd|Brai}} (Braille), their default script needs to be specified. Others do not have a default script. * Serbo-Croatian is regularly written in both the Latin and Cyrillic scripts. Because it uses two scripts, Serbo-Croatian cannot have a script class applied to entries in its category pages, as only one script class can be specified at a time. * German is usually written in the Latin script ({{cd|Latn}}), but Fraktur ({{cd|Latf}}) is also listed in its data file. So German needs an entry in the {catfix_script} list, so that the {{cd|Latn}} (Latin) script class will be applied to entries in its category pages. To find the scripts listed for a language, go to [[Module:languages]] and use the search box to find the data file for the language. To find out what a script code means, search the script code in [[Module:scripts/data]].]==] function export.catfix(lang, sc) if not lang or not lang.getCanonicalName then error('The first argument to the function "catfix" should be a language object from [[Module:languages]] or [[Module:etymology languages]].') end if sc and not sc.getCode then error('The second argument to the function "catfix" should be a script object from [[Module:scripts]].') end -- To add script classes to links on pages created by category boilerplate templates. if not sc then local code = (catfix_scripts or get_catfix_scripts())[lang:getCode()] or catfix_scripts[lang:getFullCode()] if code then sc = get_script(code) end end -- If the language only has a single valid candidate script, apply it as the default. if not sc then local scripts = lang:getScripts() if #scripts == 1 then sc = scripts[1] else -- Iterate over scripts. If there is only one when ignoring -- scripts like Brai, then pick that one. for _, script in ipairs(scripts) do if script:getCode() ~= "Brai" then if sc then -- Multiple candidates - fail. sc = nil break else sc = script end end end end end -- Hack: using a <ul> tag prevents the parser from automatically generating a <p> tag around the catfix element. return "<ul class=\"catfix\" data-anchor=\"" .. anchor_encode(lang:getFullName()) .. "\">" .. tag_text("", lang, sc) .. "</ul>" .. format_categories("Pages using catfix", nil, nil, nil, true) end --[==[ Given a type (as a string) and an arbitrary number of entities, checks whether all of those entities are language, family, script, writing system or Wikimedia language objects. Useful for error handling in functions that require one of these kinds of object. If `noErr` is set, the function returns false instead of throwing an error, which allows customised error handling to be done in the calling function.]==] function export.check_object(typ, noErr, ...) if ... == nil then if noErr then return false end error("Must provide at least one object to check.", 2) end for _, obj in ipairs{...} do if type(obj) ~= "table" or type(obj.hasType) ~= "function" then if noErr then return false end error("Function expected a " .. typ .. " object, but received a " .. type(obj) .. " instead.", 2) elseif not (typ == "object" or obj:hasType(typ)) then for _, wrong_type in ipairs{"family", "language", "script", "Wikimedia language", "writing system"} do if obj:hasType(wrong_type) then if noErr then return false end error("Function expected a " .. typ .. " object, but received a " .. wrong_type .. " object instead.", 2) end end if noErr then return false end error("Function expected a " .. typ .. " object, but received another type of object instead.", 2) end end return true end return export szkevg9xl5j9wpvvgbzhwuqjxjznt9y Module:string utilities 828 7865 54694 54383 2026-09-25T16:48:35Z Deadend0914 7211 54694 Scribunto text/plain local export = {} local function_module = "Module:fun" local load_module = "Module:load" local memoize_module = "Module:memoize" local string_char_module = "Module:string/char" local string_charset_escape_module = "Module:string/charsetEscape" local mw = mw local string = string local table = table local ustring = mw.ustring local byte = string.byte local char = string.char local concat = table.concat local find = string.find local format = string.format local gmatch = string.gmatch local gsub = string.gsub local insert = table.insert local len = string.len local lower = string.lower local match = string.match local next = next local require = require local reverse = string.reverse local select = select local sort = table.sort local sub = string.sub local tonumber = tonumber local tostring = tostring local type = type local ucodepoint = ustring.codepoint local ufind = ustring.find local ugcodepoint = ustring.gcodepoint local ugmatch = ustring.gmatch local ugsub = ustring.gsub local ulower = ustring.lower local umatch = ustring.match local unpack = unpack or table.unpack -- Lua 5.2 compatibility local upper = string.upper local usub = ustring.sub local uupper = ustring.upper local memoize = require(memoize_module) -- Defined below. local codepoint local explode_utf8 local format_fun local get_charset local gsplit local pattern_escape local pattern_simplifier local replacement_escape local title_case local trim local ucfirst local ulen --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls. ]==] local function charset_escape(...) charset_escape = require(string_charset_escape_module) return charset_escape(...) end local function is_callable(...) is_callable = require(function_module).is_callable return is_callable(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function u(...) u = require(string_char_module) return u(...) end local function prepare_iter(str, pattern, str_lib, plain) local callable = is_callable(pattern) if str_lib or plain then return pattern, #str, string, callable elseif not callable then local simple = pattern_simplifier(pattern) if simple then return simple, #str, string, false end end return pattern, ulen(str), ustring, callable end --[==[ Returns {nil} if the input value is the empty string, or otherwise the same value. If the input is a string and `do_trim` is set, the input value will be trimmed before returning; if the trimmed value is the empty string, returns {nil}. If `quote_delimiters` is set, then any outer pair of quotation marks ({' '} or {" "}) surrounding the rest of the input string will be stripped, if present. The string will not be trimmed again, converted to {nil}, or have further quotation marks stripped, as it exists as a way to embed spaces or the empty string in an input. Genuine quotation marks may also be embedded this way (e.g. {"''foo''"} returns {"'foo'"}). ]==] function export.is_not_empty(str, do_trim, quote_delimiters) if str == "" then return nil elseif not (str and type(str) == "string") then return str elseif do_trim then str = trim(str) if str == "" then return nil end end return quote_delimiters and gsub(str, "^(['\"])(.*)%1$", "%2") or str end --[==[ Explodes a string into an array of UTF-8 characters. '''Warning''': this function assumes that the input is valid UTF-8 in order to optimize speed and memory use. Passing in an input containing non-UTF-8 byte sequences could result in unexpected behaviour. ]==] function export.explode_utf8(str) local text, i = {}, 0 for ch in gmatch(str, ".[\128-\191]*") do i = i + 1 text[i] = ch end return text end explode_utf8 = export.explode_utf8 --[==[ Returns {true} if `str` is a valid UTF-8 string. This is true if, for each character, all of the following are true: * It has the expected number of bytes, which is determined by value of the leading byte: 1-byte characters are `0x00` to `0x7F`, 2-byte characters start with `0xC2` to `0xDF`, 3-byte characters start with `0xE0` to `0xEF`, and 4-byte characters start with `0xF0` to `0xF4`. * The leading byte must not fall outside of the above ranges. * The trailing byte(s) (if any), must be between `0x80` to `0xBF`. * The character's codepoint must be between U+0000 (`0x00`) and U+10FFFF (`0xF4 0x8F 0xBF 0xBF`). * The character cannot have an overlong encoding: for each byte length, the lowest theoretical encoding is equivalent to U+0000 (e.g. `0xE0 0x80 0x80`, the lowest theoretical 3-byte encoding, is exactly equivalent to U+0000). Encodings that use more than the minimum number of bytes are not considered valid, meaning that the first valid 3-byte character is `0xE0 0xA0 0x80` (U+0800), and the first valid 4-byte character is `0xF0 0x90 0x80 0x80` (U+10000). Formally, 2-byte characters have leading bytes ranging from `0xC0` to `0xDF` (rather than `0xC2` to `0xDF`), but `0xC0 0x80` to `0xC1 0xBF` are overlong encodings, so it is simpler to say that the 2-byte range begins at `0xC2`. If `allow_surrogates` is set, surrogates (U+D800 to U+DFFF) will be treated as valid UTF-8. Surrogates are used in UTF-16, which encodes codepoints U+0000 to U+FFFF with 2 bytes, and codepoints from U+10000 upwards using a pair of surrogates, which are taken together as a 4-byte unit. Since surrogates have no use in UTF-8, as it encodes higher codepoints in a different way, they are not considered valid in UTF-8 text. However, there are limited circumstances where they may be necessary: for instance, JSON escapes characters using the format `\u0000`, which must contain exactly 4 hexadecimal digits; under the scheme, codepoints above U+FFFF must be escaped as the equivalent pair of surrogates, even though the text itself must be encoded in UTF-8 (e.g. U+10000 becomes `\uD800\uDC00`). ]==] function export.isutf8(str, allow_surrogates) for ch in gmatch(str, "[\128-\255][\128-\191]*") do if #ch > 4 then return false end local b1, b2, b3, b4 = byte(ch, 1, 4) if not (b2 and b2 >= 0x80 and b2 <= 0xBF) then return false -- 1-byte is always invalid, as gmatch excludes 0x00 to 0x7F elseif not b3 then -- 2-byte if not (b1 >= 0xC2 and b1 <= 0xDF) then -- b1 == 0xC0 or b1 == 0xC1 is overlong return false end elseif not (b3 >= 0x80 and b3 <= 0xBF) then -- trailing byte return false elseif not b4 then -- 3-byte if b1 > 0xEF then return false elseif b2 < 0xA0 then if b1 < 0xE1 then -- b1 == 0xE0 and b2 < 0xA0 is overlong return false end elseif b1 < 0xE0 or (b1 == 0xED and not allow_surrogates) then -- b1 == 0xED and b2 >= 0xA0 is a surrogate return false end elseif not (b4 >= 0x80 and b4 <= 0xBF) then -- 4-byte return false elseif b2 < 0x90 then if not (b1 >= 0xF1 and b1 <= 0xF4) then -- b1 == 0xF0 and b2 < 0x90 is overlong return false end elseif not (b1 >= 0xF0 and b1 <= 0xF3) then -- b1 == 0xF4 and b2 >= 0x90 is too high return false end end return true end do local charset_chars = { ["\0"] = "%z", ["%"] = "%%", ["-"] = "%-", ["]"] = "%]", ["^"] = "%^" } charset_chars.__index = charset_chars local chars = setmetatable({ ["$"] = "%$", ["("] = "%(", [")"] = "%)", ["*"] = "%*", ["+"] = "%+", ["."] = "%.", ["?"] = "%?", ["["] = "%[" }, charset_chars) --[==[ Escapes the magic characters used in a [[mw:Extension:Scribunto/Lua reference manual#Patterns|pattern]] (Lua's version of regular expressions): {$%()*+-.?[]^}, and converts the null character to {%z}. For example, {"^$()%.[]*+-?\0"} becomes {"%^%$%(%)%%%.%[%]%*%+%-%?%z"}. This is necessary when constructing a pattern involving arbitrary text (e.g. from user input). ]==] function export.pattern_escape(str) return (gsub(str, "[%z$%%()*+%-.?[%]^]", chars)) end pattern_escape = export.pattern_escape --[==[ Escapes only {%}, which is the only magic character used in replacement [[mw:Extension:Scribunto/Lua reference manual#Patterns|patterns]] with string.gsub and mw.ustring.gsub. ]==] function export.replacement_escape(str) return (gsub(str, "%%", "%%%%")) end replacement_escape = export.replacement_escape local function case_insensitive_char(ch) local upper_ch = uupper(ch) if upper_ch == ch then ch = ulower(ch) if ch == upper_ch then return chars[ch] or ch end end return "[" .. (charset_chars[upper_ch] or upper_ch) .. (charset_chars[ch] or ch) .. "]" end local function iterate(str, str_len, text, n, start, _gsub, _sub, loc1, loc2) if not (loc1 and start <= str_len) then -- Add final chunk and return. n = n + 1 text[n] = _gsub(_sub(str, start), ".", chars) return elseif loc2 < loc1 then if _sub == sub then local b = byte(str, loc1) if b and b >= 128 then loc1 = loc1 + (b < 224 and 1 or b < 240 and 2 or 3) end end n = n + 1 text[n] = _gsub(_sub(str, start, loc1), ".", chars) start = loc1 + 1 if start > str_len then return end else -- Add chunk up to the current match. n = n + 1 text[n] = _gsub(_sub(str, start, loc1 - 1), ".", chars) -- Add current match. n = n + 1 text[n] = _gsub(_sub(str, loc1, loc2), ".", case_insensitive_char) start = loc2 + 1 end return n, start end --[==[ Escapes the magic characters used in a [[mw:Extension:Scribunto/Lua reference manual#Patterns|pattern]], and makes all characters case-insensitive. An optional pattern or find function (see {split}) may be supplied as the second argument, the third argument (`str_lib`) forces use of the string library, while the fourth argument (`plain`) turns any pattern matching facilities off in the optional pattern supplied. ]==] function export.case_insensitive_pattern(str, pattern_or_func, str_lib, plain) if pattern_or_func == nil then return (gsub(str, str_lib and "[^\128-\255]" or ".[\128-\191]*", case_insensitive_char)) end local text, n, start, str_len, _string, callable = {}, 0, 1 pattern_or_func, str_len, _string, callable = prepare_iter(str, pattern_or_func, str_lib, plain) local _find, _gsub, _sub = _string.find, _string.gsub, _string.sub if callable then repeat n, start = iterate(str, str_len, text, n, start, _gsub, _sub, pattern_or_func(str, start)) until not start -- Special case if the pattern is anchored to the start: "^" always -- anchors to the start position, not the start of the string, so get -- around this by only attempting one match with the pattern, then match -- the end of the string. elseif byte(pattern_or_func) == 0x5E then -- ^ n, start = iterate(str, str_len, text, n, start, _gsub, _sub, _find(str, pattern_or_func, start, plain)) if start ~= nil then iterate(str, str_len, text, n, start, _gsub, _sub, _find(str, "$", start, plain)) end else repeat n, start = iterate(str, str_len, text, n, start, _gsub, _sub, _find(str, pattern_or_func, start, plain)) until not start end return concat(text) end end do local character_classes local function get_character_classes() character_classes, get_character_classes = { [0x41] = true, [0x61] = true, -- Aa [0x43] = true, [0x63] = true, -- Cc [0x44] = true, [0x64] = true, -- Dd [0x4C] = true, [0x6C] = true, -- Ll [0x50] = true, [0x70] = true, -- Pp [0x53] = true, [0x73] = true, -- Ss [0x55] = true, [0x75] = true, -- Uu [0x57] = true, [0x77] = true, -- Ww [0x58] = true, [0x78] = true, -- Xx [0x5A] = true, -- z dealt with separately. }, nil return character_classes end local function check_sets_equal(set1, set2) local k2 for k1, v1 in next, set1 do local v2 = set2[k1] if v1 ~= v2 and (v2 == nil or not check_sets_equal(v1, v2)) then return false end k2 = next(set2, k2) end return next(set2, k2) == nil end local function check_sets(bytes) local key, set1, set = next(bytes) if set1 == true then return true elseif not check_sets(set1) then return false end while true do key, set = next(bytes, key) if not key then return true elseif not check_sets_equal(set, set1) then return false end end end local function make_charset(range) if #range == 1 then return char(range[1]) end sort(range) local compressed, n, start = {}, 0, range[1] for i = 1, #range do local this, nxt = range[i], range[i + 1] if nxt ~= this + 1 then n = n + 1 compressed[n] = this == start and char(this) or char(start) .. "-" .. char(this) start = nxt end end return "[" .. concat(compressed) .. "]" end local function parse_1_byte_charset(pattern, pos) local ch while true do pos, ch = match(pattern, "()([%%%]\192-\255])", pos) if ch == "%" then local nxt = byte(pattern, pos + 1) if not nxt or nxt >= 128 or (character_classes or get_character_classes())[nxt] then -- acdlpsuwxACDLPSUWXZ, but not z return false end pos = pos + 2 elseif ch == "]" then pos = pos + 1 return pos else return false end end end --[==[ Parses `pattern`, a ustring library pattern, and attempts to convert it into a string library pattern. If conversion isn't possible, returns false. ]==] function pattern_simplifier(pattern) if type(pattern) == "number" then return tostring(pattern) end local pos, capture_groups, start, n, output, ch, nxt_pos = 1, 0, 1, 0 while true do -- FIXME: use "()([%%(.[\128-\255])[\128-\191]?[\128-\191]?[\128-\191]?()" and ensure non-UTF8 always fails. pos, ch, nxt_pos = match(pattern, "()([%%(.[\192-\255])[\128-\191]*()", pos) if not ch then break end local nxt = byte(pattern, nxt_pos) if ch == "%" then if nxt == 0x62 then -- b local nxt2, nxt3 = byte(pattern, pos + 2, pos + 3) if not (nxt2 and nxt2 < 128 and nxt3 and nxt3 < 128) then return false end pos = pos + 4 elseif nxt == 0x66 then -- f nxt_pos = nxt_pos + 2 local nxt2, nxt3 = byte(pattern, nxt_pos - 1, nxt_pos) -- Only possible to convert a positive %f charset which is -- all ASCII, so use parse_1_byte_charset. if not (nxt2 == 0x5B and nxt3 and nxt3 ~= 0x5E and nxt3 < 128) then -- [^ return false elseif nxt3 == 0x5D then -- Initial ] is non-magic. nxt_pos = nxt_pos + 1 end pos = parse_1_byte_charset(pattern, nxt_pos) if not pos then return false end elseif nxt == 0x5A then -- Z nxt = byte(pattern, nxt_pos + 1) if nxt == 0x2A or nxt == 0x2D then -- *- pos = pos + 3 else if output == nil then output = {} end local ins = sub(pattern, start, pos - 1) .. "[\1-\127\192-\255]" n = n + 1 if nxt == 0x2B then -- + output[n] = ins .. "%Z*" pos = pos + 3 elseif nxt == 0x3F then -- ? output[n] = ins .. "?[\128-\191]*" pos = pos + 3 else output[n] = ins .. "[\128-\191]*" pos = pos + 2 end start = pos end elseif not nxt or (character_classes or get_character_classes())[nxt] then -- acdlpsuwxACDLPSUWX, but not Zz return false -- Skip the next character if it's ASCII. Otherwise, we will -- still need to do length checks. else pos = pos + (nxt < 128 and 2 or 1) end elseif ch == "(" then if nxt == 0x29 or capture_groups == 32 then -- ) return false end capture_groups = capture_groups + 1 pos = pos + 1 elseif ch == "." then if nxt == 0x2A or nxt == 0x2D then -- *- pos = pos + 2 else if output == nil then output = {} end local ins = sub(pattern, start, pos - 1) .. "[^\128-\191]" n = n + 1 if nxt == 0x2B then -- + output[n] = ins .. ".*" pos = pos + 2 elseif nxt == 0x3F then -- ? output[n] = ins .. "?[\128-\191]*" pos = pos + 2 else output[n] = ins .. "[\128-\191]*" pos = pos + 1 end start = pos end elseif ch == "[" then -- Fail negative charsets. TODO: 1-byte charsets should be safe. if nxt == 0x5E then -- ^ return false -- If the first character is "%", ch_len is determined by the -- next one instead. elseif nxt == 0x25 then -- % nxt = byte(pattern, nxt_pos + 1) elseif nxt == 0x5D then -- Initial ] is non-magic. nxt_pos = nxt_pos + 1 end if not nxt then return false end local ch_len = nxt < 128 and 1 or nxt < 224 and 2 or nxt < 240 and 3 or 4 if ch_len == 1 then -- Single-byte charset. pos = parse_1_byte_charset(pattern, nxt_pos) if not pos then return false end else -- Multibyte charset. -- TODO: 1-byte chars should be safe to mix with multibyte chars. CONFIRM THIS FIRST. local charset_pos, bytes = pos pos = pos + 1 while true do -- TODO: non-ASCII charset ranges. pos, ch, nxt_pos = match(pattern, "^()([^\128-\191])[\128-\191]*()", pos) -- If escaped, get the next character. No need to -- distinguish magic characters or character classes, -- as they'll all fail for having the wrong length -- anyway. if ch == "%" then pos, ch, nxt_pos = match(pattern, "^()([^\128-\191])[\128-\191]*()", nxt_pos) elseif ch == "]" then pos = nxt_pos break end if not (ch and nxt_pos - pos == ch_len) then return false elseif bytes == nil then bytes = {} end local bytes, last = bytes, nxt_pos - 1 for i = pos, last - 1 do local b = byte(pattern, i) local bytes_b = bytes[b] if bytes_b == nil then bytes_b = {} bytes[b] = bytes_b end bytes[b], bytes = bytes_b, bytes_b end bytes[byte(pattern, last)] = true pos = nxt_pos end if not pos then return false end nxt = byte(pattern, pos) if ( (nxt == 0x2A or nxt == 0x2D or nxt == 0x3F) or -- *-? (nxt == 0x2B and ch_len > 2) or -- + not check_sets(bytes) ) then return false end local ranges, b, key, next_byte = {}, 0 repeat key, next_byte = next(bytes) local range, n = {key}, 1 -- Loop starts on the second iteration. for key in next, bytes, key do n = n + 1 range[n] = key end b = b + 1 ranges[b] = range bytes = next_byte until next_byte == true if nxt == 0x2B then -- + local range1, range2 = ranges[1], ranges[2] ranges[1], ranges[3] = make_charset(range1), make_charset(range2) local n = #range2 for i = 1, #range1 do n = n + 1 range2[n] = range1[i] end ranges[2] = make_charset(range2) .. "*" pos = pos + 1 else for i = 1, #ranges do ranges[i] = make_charset(ranges[i]) end end if output == nil then output = {} end nxt = byte(pattern, pos) n = n + 1 output[n] = sub(pattern, start, charset_pos - 1) .. concat(ranges) .. ((nxt == 0x2A or nxt == 0x2B or nxt == 0x2D or nxt == 0x3F) and "%" or "") -- following *+-? now have to be escaped start = pos end elseif not nxt then break elseif nxt == 0x2B then -- + if nxt_pos - pos ~= 2 then return false elseif output == nil then output = {} end pos, nxt_pos = pos + 1, nxt_pos + 1 nxt = byte(pattern, nxt_pos) local ch2 = sub(pattern, pos, pos) n = n + 1 output[n] = sub(pattern, start, pos - 1) .. "[" .. ch .. ch2 .. "]*" .. ch2 .. ((nxt == 0x2A or nxt == 0x2B or nxt == 0x2D or nxt == 0x3F) and "%" or "") -- following *+-? now have to be escaped pos, start = nxt_pos, nxt_pos elseif nxt == 0x2A or nxt == 0x2D or nxt == 0x3F then -- *-? return false else pos = nxt_pos end end if start == 1 then return pattern end return concat(output) .. sub(pattern, start) end pattern_simplifier = memoize(pattern_simplifier, true) export.pattern_simplifier = pattern_simplifier end --[==[ Parses `charset`, the interior of a string or ustring library character set, and normalizes it into a string or ustring library pattern (e.g. {"abcd-g"} becomes {"[abcd-g]"}, and {"[]"} becomes {"[[%]]"}). The negative (`^`), range (`-`) and literal (`%`) magic characters work as normal, and character classes may be used (e.g. `%d` and `%w`), but opening and closing square brackets are sanitized so that they behave like ordinary characters. ]==] function get_charset(charset) if type(charset) == "number" then return tostring(charset) end local pos, start, n, output = 1, 1, 0 if byte(charset) == 0x5E then -- ^ pos = pos + 1 end -- FIXME: "]" is non-magic if it's the first character in a charset. local nxt_pos, nxt while true do local new_pos, ch = match(charset, "()([%%%-%]])", pos) if not ch then break -- Skip percent escapes. Ranges can't start with them, either. elseif ch == "%" then pos = new_pos + 2 else -- If `ch` is a hyphen, get the character before iff it's at or ahead of `pos`. if ch == "-" and new_pos > pos then pos, nxt_pos, nxt = new_pos - 1, new_pos, ch ch = sub(charset, pos, pos) else pos, nxt_pos = new_pos, new_pos + 1 nxt = sub(charset, nxt_pos, nxt_pos) end -- Range. if nxt == "-" then if output == nil then output = {} end n = n + 1 output[n] = sub(charset, start, pos - 1) nxt_pos = nxt_pos + 1 nxt = sub(charset, nxt_pos, nxt_pos) -- Ranges fail if they end with a percent escape, so escape the hyphen to avoid undefined behaviour. if nxt == "" or nxt == "%" then n = n + 1 output[n] = (ch == "]" and "%]" or ch) .. "%-" start = nxt_pos nxt_pos = nxt_pos + 2 -- Since ranges can't contain "%]", since it's escaped, range inputs like "]-z" or "a-]" must be -- adjusted to the character before or after, plus "%]" (e.g. "%]^-z" or "a-\\%]"). The escaped "%]" is -- omitted if the range would be empty (i.e. if the first byte is greater than the second). else n = n + 1 output[n] = (ch == "]" and (byte(nxt) >= 0x5D and "%]^" or "^") or ch) .. "-" .. (nxt == "]" and (byte(ch) <= 0x5D and "\\%]" or "\\") or nxt) nxt_pos = nxt_pos + 1 start = nxt_pos end elseif ch == "-" or ch == "]" then if output == nil then output = {} end n = n + 1 output[n] = sub(charset, start, pos - 1) .. "%" .. ch start = nxt_pos end pos = nxt_pos end end if start == 1 then return "[" .. charset .. "]" end return "[" .. concat(output) .. sub(charset, start) .. "]" end get_charset = memoize(get_charset, true) export.get_charset = get_charset function export.len(str) return type(str) == "number" and len(str) or #str - #gsub(str, "[^\128-\191]+", "") end ulen = export.len function export.sub(str, i, j) str, i = type(str) == "number" and tostring(str) or str, i or 1 if i < 0 or j and j < 0 then return usub(str, i, j) elseif j and i > j or i > #str then return "" end local n, new_i = 0 for loc1, loc2 in gmatch(str, "()[^\128-\191]+()[\128-\191]*") do n = n + loc2 - loc1 if not new_i and n >= i then new_i = loc2 - (n - i) - 1 if not j then return sub(str, new_i) end end if j and n > j then return sub(str, new_i, loc2 - (n - j) - 1) end end return new_i and sub(str, new_i) or "" end do local function _find(str, loc1, loc2, ...) if loc1 and not match(str, "^()[^\128-\255]*$") then -- Use raw values of loc1 and loc2 to get loc1 and the length of the match. loc1, loc2 = ulen(sub(str, 1, loc1)), ulen(sub(str, loc1, loc2)) -- Offset length with loc1 to get loc2. loc2 = loc1 + loc2 - 1 end return loc1, loc2, ... end --[==[A version of find which uses string.find when possible, but otherwise uses mw.ustring.find.]==] function export.find(str, pattern, init, plain) init = init or 1 if init ~= 1 and not match(str, "^()[^\128-\255]*$") then return ufind(str, pattern, init, plain) elseif plain then return _find(str, find(str, pattern, init, true)) end local simple = pattern_simplifier(pattern) if simple then return _find(str, find(str, simple, init)) end return ufind(str, pattern, init) end end --[==[A version of match which uses string.match when possible, but otherwise uses mw.ustring.match.]==] function export.match(str, pattern, init) init = init or 1 if init ~= 1 and not match(str, "^()[^\128-\255]*$") then return umatch(str, pattern, init) end local simple = pattern_simplifier(pattern) if simple then return match(str, simple, init) end return umatch(str, pattern, init) end --[==[A version of gmatch which uses string.gmatch when possible, but otherwise uses mw.ustring.gmatch.]==] function export.gmatch(str, pattern) local simple = pattern_simplifier(pattern) if simple then return gmatch(str, simple) end return ugmatch(str, pattern) end --[==[A version of gsub which uses string.gsub when possible, but otherwise uses mw.ustring.gsub.]==] function export.gsub(str, pattern, repl, n) local simple = pattern_simplifier(pattern) if simple then return gsub(str, simple, repl, n) end return ugsub(str, pattern, repl, n) end --[==[ Like gsub, but pattern-matching facilities are turned off, so `pattern` and `repl` (if a string) are treated as literal. ]==] function export.plain_gsub(str, pattern, repl, n) return gsub(str, pattern_escape(pattern), type(repl) == "string" and replacement_escape(repl) or repl, n) end --[==[ Reverses a UTF-8 string; equivalent to string.reverse. ]==] function export.reverse(str) return reverse((gsub(str, "[\192-\255][\128-\191]*", reverse))) end function export.char(...) -- To be moved to [[Module:string/char]]. return u(...) end do local function utf8_err(func_name) error(format("bad argument #1 to '%s' (string is not UTF-8)", func_name), 4) end local function get_codepoint(func_name, b1, b2, b3, b4) if b1 <= 0x7F then return b1, 1 elseif not (b2 and b2 >= 0x80 and b2 <= 0xBF) then utf8_err(func_name) elseif b1 <= 0xDF then local cp = 0x40 * b1 + b2 - 0x3080 return cp >= 0x80 and cp or utf8_err(func_name), 2 elseif not (b3 and b3 >= 0x80 and b3 <= 0xBF) then utf8_err(func_name) elseif b1 <= 0xEF then local cp = 0x1000 * b1 + 0x40 * b2 + b3 - 0xE2080 return cp >= 0x800 and cp or utf8_err(func_name), 3 elseif not (b4 and b4 >= 0x80 and b4 <= 0xBF) then utf8_err(func_name) end local cp = 0x40000 * b1 + 0x1000 * b2 + 0x40 * b3 + b4 - 0x3C82080 return cp >= 0x10000 and cp <= 0x10FFFF and cp or utf8_err(func_name), 4 end function export.codepoint(str, i, j) if str == "" then return -- return nothing elseif type(str) == "number" then return byte(str, i, j) end i, j = i or 1, j == -1 and #str or i or 1 if i == 1 and j == 1 then return (get_codepoint("codepoint", byte(str, 1, 4))) elseif i < 0 or j < 0 then return ucodepoint(str, i, j) -- FIXME end local n, nb, ret, nr = 0, 1, {}, 0 while n < j do n = n + 1 if n < i then local b = byte(str, nb) nb = nb + (b < 128 and 1 or b < 224 and 2 or b < 240 and 3 or 4) else local b1, b2, b3, b4 = byte(str, nb, nb + 3) if not b1 then break end nr = nr + 1 local add ret[nr], add = get_codepoint("codepoint", b1, b2, b3, b4) nb = nb + add end end return unpack(ret) end codepoint = export.codepoint function export.gcodepoint(str, i, j) i, j = i or 1, j ~= -1 and j or nil if i < 0 or j and j < 0 then return ugcodepoint(str, i, j) -- FIXME end local n, nb = 1, 1 while n < i do local b = byte(str, nb) if not b then break end nb = nb + (b < 128 and 1 or b < 224 and 2 or b < 240 and 3 or 4) n = n + 1 end return function() if j and n > j then return nil end n = n + 1 local b1, b2, b3, b4 = byte(str, nb, nb + 3) if not b1 then return nil end local ret, add = get_codepoint("gcodepoint", b1, b2, b3, b4) nb = nb + add return ret end end end do local _ulower = ulower --[==[A version of lower which uses string.lower when possible, but otherwise uses mw.ustring.lower.]==] function export.lower(str) return (match(str, "^()[^\128-\255]*$") and lower or _ulower)(str) end end do local _uupper = uupper --[==[A version of upper which uses string.upper when possible, but otherwise uses mw.ustring.upper.]==] function export.upper(str) return (match(str, "^()[^\128-\255]*$") and upper or _uupper)(str) end end do local function add_captures(t, n, ...) if ... == nil then return end -- Insert any captures from the splitting pattern. local offset, capture = n - 1, ... while capture do n = n + 1 t[n] = capture capture = select(n - offset, ...) end return n end --[==[ Reimplementation of mw.text.split() that includes any capturing groups in the splitting pattern. This works like Python's re.split() function, except that it has Lua's behavior when the split pattern is empty (i.e. advancing by one character at a time; Python returns the whole remainder of the string). When possible, it will use the string library, but otherwise uses the ustring library. There are two optional parameters: `str_lib` forces use of the string library, while `plain` turns any pattern matching facilities off, treating `pattern` as literal. In addition, `pattern` may be a custom find function (or callable table), which takes the input string and start index as its two arguments, and must return the start and end index of the match, plus any optional captures, or nil if there are no further matches. By default, the start index will be calculated using the ustring library, unless `str_lib` or `plain` is set. ]==] function export.split(str, pattern_or_func, str_lib, plain) local iter, t, n = gsplit(str, pattern_or_func, str_lib, plain), {}, 0 repeat n = add_captures(t, n, iter()) until n == nil return t end export.capturing_split = export.split -- To be removed. end --[==[ Returns an iterator function, which iterates over the substrings returned by {split}. The first value returned is the string up the splitting pattern, with any capture groups being returned as additional values on that iteration. ]==] function export.gsplit(str, pattern_or_func, str_lib, plain) local start, final, str_len, _string, callable = 1 pattern_or_func, str_len, _string, callable = prepare_iter(str, pattern_or_func, str_lib, plain) local _find, _sub = _string.find, _string.sub local function iter(loc1, loc2, ...) -- If no match, or there is but we're past the end of the string -- (which happens when the match is the empty string), then return -- the final chunk. if not loc1 then final = true return _sub(str, start) end -- Special case: If we match the empty string, then eat the -- next character; this avoids an infinite loop, and makes -- splitting by the empty string work the way mw.text.gsplit() does -- (including non-adjacent empty string matches with %f). If we -- reach the end of the string this way, set `final` to true, so we -- don't get stuck matching the empty string at the end. local chunk if loc2 < loc1 then -- If using the string library, we need to make sure we advance -- by one UTF-8 character. if _sub == sub then local b = byte(str, loc1) if b and b >= 128 then loc1 = loc1 + (b < 224 and 1 or b < 240 and 2 or 3) end end chunk = _sub(str, start, loc1) if loc1 >= str_len then final = true else start = loc1 + 1 end -- Eat chunk up to the current match. else chunk = _sub(str, start, loc1 - 1) start = loc2 + 1 end return chunk, ... end if callable then return function() if not final then return iter(pattern_or_func(str, start)) end end -- Special case if the pattern is anchored to the start: "^" always -- anchors to the start position, not the start of the string, so get -- around this by only attempting one match with the pattern, then match -- the end of the string. elseif byte(pattern_or_func) == 0x5E then -- ^ local returned return function() if not returned then returned = true return iter(_find(str, pattern_or_func, start, plain)) elseif not final then return iter(_find(str, "$", start, plain)) end end end return function() if not final then return iter(_find(str, pattern_or_func, start, plain)) end end end gsplit = export.gsplit function export.count(str, pattern, plain) if plain then return select(2, gsub(str, pattern_escape(pattern), "")) end local simple = pattern_simplifier(pattern) if simple then return select(2, gsub(str, pattern, "")) end return select(2, ugsub(str, pattern, "")) end function export.trim(str, charset, str_lib, plain) if charset == nil then -- "^.*%S" is the fastest trim algorithm except when strings only consist of characters to be trimmed, which are -- very slow due to catastrophic backtracking. gsub with "^%s*" gets around this by trimming such strings to "" -- first. return match(gsub(str, "^%s*", ""), "^.*%S") or "" elseif charset == "" then return str end charset = plain and ("[" .. charset_escape(charset) .. "]") or get_charset(charset) -- The pattern uses a non-greedy quantifier instead of the algorithm used for %s, because negative character sets -- are non-trivial to compute (e.g. "[^^-z]" becomes "[%^_-z]"). Plus, if the ustring library has to be used, there -- would be two callbacks into PHP, which is slower. local pattern = "^" .. charset .. "*(.-)" .. charset .. "*$" if not str_lib then local simple = pattern_simplifier(pattern) if not simple then return umatch(str, pattern) end pattern = simple end return match(str, pattern) end trim = export.trim do local entities local function get_entities() entities, get_entities = load_data("Module:data/entities"), nil return entities end local function decode_entity(hash, x, code) if hash == "" then return (entities or get_entities())[x .. code] end local cp if x == "" then cp = match(code, "^()%d+$") and tonumber(code) else cp = match(code, "^()%x+$") and tonumber(code, 16) end return cp and (cp <= 0xD7FF or cp >= 0xE000 and cp <= 0x10FFFF) and u(cp) or nil end -- Non-ASCII characters aren't valid in proper HTML named entities, but MediaWiki uses them in some custom aliases -- which have also been included in [[Module:data/entities]]. function export.decode_entities(str) local amp = find(str, "&", nil, true) return amp and find(str, ";", amp, true) and gsub(str, "&(#?)([xX]?)([%w\128-\255]+);", decode_entity) or str end end do local entities local function get_entities() -- Memoized HTML entities (taken from mw.text.lua). entities, get_entities = { ["\""] = "&quot;", ["&"] = "&amp;", ["'"] = "&#039;", ["<"] = "&lt;", [">"] = "&gt;", ["\194\160"] = "&nbsp;", }, nil return entities end local function encode_entity(ch) local entity = (entities or get_entities())[ch] if entity == nil then local cp = codepoint(ch) -- U+D800 to U+DFFF are surrogates, so can't be encoded as entities. entity = cp and (cp <= 0xD7FF or cp >= 0xE000) and format("&#%d;", cp) or false entities[ch] = entity end return entity or nil end function export.encode_entities(str, charset, str_lib, plain) if charset == nil then return (gsub(str, "[\"&'<>\194]\160?", entities or get_entities())) elseif charset == "" then return str end local pattern = plain and ("[" .. charset_escape(charset) .. "]") or charset == "." and charset or get_charset(charset) if not str_lib then local simple = pattern_simplifier(pattern) if not simple then return (ugsub(str, pattern, encode_entity)) end pattern = simple end return (gsub(str, pattern, encode_entity)) end end do local function decode_path(code) return char(tonumber(code, 16)) end local function decode(lead, trail) if lead == "+" or lead == "_" then return " " .. trail elseif #trail == 2 then return decode_path(trail) end return lead .. trail end function export.decode_uri(str, enctype) enctype = enctype and upper(enctype) or "QUERY" if enctype == "PATH" then return find(str, "%", nil, true) and gsub(str, "%%(%x%x)", decode_path) or str elseif enctype == "QUERY" then return (find(str, "%", nil, true) or find(str, "+", nil, true)) and gsub(str, "([%%%+])(%x?%x?)", decode) or str elseif enctype == "WIKI" then return (find(str, "%", nil, true) or find(str, "_", nil, true)) and gsub(str, "([%%_])(%x?%x?)", decode) or str end error("bad argument #2 to 'decode_uri' (expected QUERY, PATH, or WIKI)", 2) end end do local function _remove_comments(str, pre) local head = find(str, "<!--", nil, true) if not head then return str end local ret, n = {sub(str, 1, head - 1)}, 1 while true do local loc = find(str, "-->", head + 4, true) if not loc then return pre and concat(ret) or concat(ret) .. sub(str, head) end head = loc + 3 loc = find(str, "<!--", head, true) if not loc then return concat(ret) .. sub(str, head) end n = n + 1 ret[n] = sub(str, head, loc - 1) head = loc end end --[==[ Removes any HTML comments from the input text. `stage` can be one of three options: * {"PRE"} (default) applies the method used by MediaWiki's preprocessor: all {{code|html|<nowiki><!-- ... --></nowiki>}} pairs are removed, as well as any text after an unclosed {{code|html|<nowiki><!--</nowiki>}}. This is generally suitable when parsing raw template or [[mw:Parser extension tags|parser extension tag]] code. (Note, however, that the actual method used by the preprocessor is considerably more complex and differs under certain conditions (e.g. comments inside nowiki tags); if full accuracy is absolutely necessary, use [[Module:template parser]] instead). * {"POST"} applies the method used to generate the final page output once all templates have been expanded: it loops over the text, removing any {{code|html|<nowiki><!-- ... --></nowiki>}} pairs until no more are found (e.g. {{code|html|<nowiki><!-<!-- ... -->- ... --></nowiki>}} would be fully removed), but any unclosed {{code|html|<nowiki><!--</nowiki>}} is ignored. This is suitable for handling links embedded in template inputs, where the {"PRE"} method will have already been applied by the native parser. * {"BOTH"} applies {"PRE"} then {"POST"}. ]==] function export.remove_comments(str, stage) if not stage or stage == "PRE" then return _remove_comments(str, true) end local processed = stage == "POST" and _remove_comments(str) or stage == "BOTH" and _remove_comments(str, true) or error("bad argument #2 to 'remove_comments' (expected PRE, POST, or BOTH)", 2) while processed ~= str do str = processed processed = _remove_comments(str) end return str end end do local byte_escapes local function get_byte_escapes() byte_escapes, get_byte_escapes = load_data("Module:string utilities/data").byte_escapes, nil return byte_escapes end local function escape_byte(b) return (byte_escapes or get_byte_escapes())[b] or format("\\%03d", byte(b)) end function export.escape_bytes(str) return (gsub(str, ".", escape_byte)) end end function export.format_fun(str, fun) return (gsub(str, "{(\\?)((\\?)[^{}]*)}", function(p1, name, p2) if #p1 + #p2 == 1 then return name == "op" and "{" or name == "cl" and "}" or error(mw.getCurrentFrame():getTitle() .. " format: unrecognized escape sequence '{\\" .. name .. "}'") elseif fun(name) and type(fun(name)) ~= "string" then error(mw.getCurrentFrame():getTitle() .. " format: \"" .. name .. "\" is a " .. type(fun(name)) .. ", not a string") end return fun(name) or error(mw.getCurrentFrame():getTitle() .. " format: \"" .. name .. "\" not found in table") end)) end format_fun = export.format_fun --[==[ This function, unlike {string.format} and {mw.ustring.format}, takes just two parameters, a format string and a table, and replaces all instances of { {param_name} } in the format string with the table's entry for {param_name}. The opening and closing brace characters can be escaped with { {\op} } and { {\cl} }, respectively. A table entry beginning with a slash can be escaped by doubling the initial slash. ====Examples==== * {string_utilities.format("{foo} fish, {bar} fish, {baz} fish, {quux} fish", {["foo"]="one", ["bar"]="two", ["baz"]="red", ["quux"]="blue"}) } *: produces: {"one fish, two fish, red fish, blue fish"} * {string_utilities.format("The set {\\op}1, 2, 3{\\cl} contains {\\\\hello} elements.", {["\\hello"]="three"})} *: produces: {"The set {1, 2, 3} contains three elements."} *:* Note that the single and double backslashes should be entered as double and quadruple backslashes when quoted in a literal string. ]==] function export.format(str, tbl) return format_fun(str, function(key) return tbl[key] end) end do local function do_uclcfirst(str, case_func) -- Re-case the first letter. local first, remainder = match(str, "^(.[\128-\191]*)(.*)") return first and (case_func(first) .. remainder) or "" end local function uclcfirst(str, case_func) -- Strip off any HTML tags at the beginning. This currently does not handle comments or <ref>...</ref> -- correctly; it's intended for text wrapped in <span> or the like, as happens when passing text through -- [[Module:links]]. local html_at_beginning = nil if str:match("^<") then while true do local html_tag, rest = str:match("^(<.->)(.*)$") if not html_tag then break end if not html_at_beginning then html_at_beginning = {} end insert(html_at_beginning, html_tag) str = rest end end -- If there's a link at the beginning, re-case the first letter of the -- link text. This pattern matches both piped and unpiped links. -- If the link is not piped, the second capture (linktext) will be empty. local link, linktext, remainder = match(str, "^%[%[([^|%]]+)%|?(.-)%]%](.*)$") local retval if link then retval = "[[" .. link .. "|" .. do_uclcfirst(linktext ~= "" and linktext or link, case_func) .. "]]" .. remainder else retval = do_uclcfirst(str, case_func) end if html_at_beginning then retval = concat(html_at_beginning) .. retval end return retval end --[==[ Uppercase the first character of the input string, correctly handling one-part and two-part links, optionally surrounded by HTML tags such as `<nowiki><span>...</span></nowiki>`, possibly nested. Intended to correctly uppercase the first character of text that may include links that have been passed through `full_link()` in [[Module:links]] or a similar function. ]==] function export.ucfirst(str) return uclcfirst(str, uupper) end ucfirst = export.ucfirst --[==[ Lowercase the first character of the input string, correctly handling one-part and two-part links, optionally surrounded by HTML tags such as `<nowiki><span>...</span></nowiki>`, possibly nested. Intended to correctly lowercase the first character of text that may include links that have been passed through `full_link()` in [[Module:links]] or a similar function. ]==] function export.lcfirst(str) return uclcfirst(str, ulower) end --[==[Capitalizes each word of the input string. WARNING: May be broken in the presence of multiword links.]==] function export.capitalize(str) -- Capitalize multi-word that is separated by spaces -- by uppercasing the first letter of each part. return (ugsub(str, "%w+", ucfirst)) end local function do_title_case(first, remainder) first = uupper(first) return remainder == "" and first or (first .. ulower(remainder)) end --[==[ Capitalizes each word of the input string, with any further letters in each word being converted to lowercase. ]==] function export.title_case(str) return str == "" and "" or ugsub(str, "(%w)(%w*)", do_title_case) end title_case = export.title_case --[==[ Converts the input string to {{w|Camel case|CamelCase}}. Any non-word characters are treated as breaks between words. If `lower_first` is set, then the first character of the string will be lowercase (e.g. camelCase). ]==] function export.camel_case(str, lower_first) str = ugsub(str, "%W*(%w*)", title_case) return lower_first and do_uclcfirst(str, ulower) or str end end do local function do_snake_case(nonword, word) return nonword == "" and word or "_" .. word end --[==[ Converts the input string to {{w|Snake case|snake_case}}. Any non-word characters are treated as breaks between words. ]==] function export.snake_case(str) return (ugsub(str, "(%W*)(%w*)", do_snake_case)) end end return export ndjfpg4lokleft04xzto19wigx7fczh Module:parameters 828 7866 54696 54384 2026-09-25T16:53:15Z Deadend0914 7211 54696 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).numKeys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export c23kzesggrlowmn06oii7zu1bbzht4t 54739 54696 2026-09-25T21:34:57Z Deadend0914 7211 54739 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function numKeys(...) numKeys = require(table_module).numKeys return numKeys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, numKeys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(numKeys(val), numKeys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use numKeys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = numKeys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export psuei78pgjnvv9rrtgoosgv59ju0xax 54741 54739 2026-09-25T21:38:55Z Deadend0914 7211 54741 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).num_keys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export 4h2drxwjwq8uxtyaba4l7kklppsog45 54744 54741 2026-09-25T21:54:06Z Deadend0914 7211 54744 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).numKeys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export c23kzesggrlowmn06oii7zu1bbzht4t Module:languages 828 7868 54721 54386 2026-09-25T18:19:33Z Deadend0914 7211 54721 Scribunto text/plain --[==[ intro: This module implements fetching of language-specific information and processing text in a given language. ===Types of languages=== There are two types of languages: full languages and etymology-only languages. The essential difference is that only full languages appear in L2 headings in vocabulary entries, and hence categories like [[:Category:French nouns]] exist only for full languages. Etymology-only languages have either a full language or another etymology-only language as their parent (in the parent-child inheritance sense), and for etymology-only languages with another etymology-only language as their parent, a full language can always be derived by following the parent links upwards. For example, "Canadian French", code `fr-CA`, is an etymology-only language whose parent is the full language "French", code `fr`. An example of an etymology-only language with another etymology-only parent is "Northumbrian Old English", code `ang-nor`, which has "Anglian Old English", code `ang-ang` as its parent; this is an etymology-only language whose parent is "Old English", code `ang`, which is a full language. (This is because Northumbrian Old English is considered a variety of Anglian Old English.) Sometimes the parent is the "Undetermined" language, code `und`; this is the case, for example, for "substrate" languages such as "Pre-Greek", code `qsb-grc`, and "the BMAC substrate", code `qsb-bma`. It is important to distinguish language ''parents'' from language ''ancestors''. The parent-child relationship is one of containment, i.e. if X is a child of Y, X is considered a variety of Y. On the other hand, the ancestor-descendant relationship is one of descent in time. For example, "Classical Latin", code `la-cla`, and "Late Latin", code `la-lat`, are both etymology-only languages with "Latin", code `la`, as their parents, because both of the former are varieties of Latin. However, Late Latin does *NOT* have Classical Latin as its parent because Late Latin is *not* a variety of Classical Latin; rather, it is a descendant. There is in fact a separate `ancestors` field that is used to express the ancestor-descendant relationship, and Late Latin's ancestor is given as Classical Latin. It is also important to note that sometimes an etymology-only language is actually the conceptual ancestor of its parent language. This happens, for example, with "Old Italian" (code `roa-oit`), which is an etymology-only variant of full language "Italian" (code `it`), and with "Old Latin" (code `itc-ola`), which is an etymology-only variant of Latin. In both cases, the full language has the etymology-only variant listed as an ancestor. This allows a Latin term to inherit from Old Latin using the {{tl|inh}} template (where in this template, "inheritance" refers to ancestral inheritance, i.e. inheritance in time, rather than in the parent-child sense); likewise for Italian and Old Italian. Full languages come in three subtypes: * {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted in the main namespace. There may also be reconstructed terms for the language, which are placed in the {Reconstruction} namespace and must be prefixed with * to indicate a reconstruction. Most full languages are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto and Volapük, among others) are also allowed in the mainspace and considered regular languages. * {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the {Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed with *. Languages such as Proto-Indo-European and Proto-Germanic are in this category. * {appendix-constructed}: This language is attested but does not meet the additional requirements set out for constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore be in the Appendix namespace, but they are not reconstructed and therefore should not have * prefixed in links. Most constructed languages are of this subtype. Both full languages and etymology-only languages have a {Language} object associated with them, which is fetched using the {getByCode} function in [[Module:languages]] to convert a language code to a {Language} object. Depending on the options supplied to this function, etymology-only languages may or may not be accepted, and family codes may be accepted (returning a {Family} object as described in [[Module:families]]). There are also separate {getByCanonicalName} functions in [[Module:languages]] and [[Module:etymology languages]] to convert a language's canonical name to a {Language} object (depending on whether the canonical name refers to a full or etymology-only language). ===Textual representations=== Textual strings belonging to a given language come in several different ''text variants'': # The ''input text'' is what the user supplies in wikitext, in the parameters to {{tl|m}}, {{tl|l}}, {{tl|ux}}, {{tl|t}}, {{tl|lang}} and the like. # The ''corrected input text'' is the input text with some corrections and/or normalizations applied, such as bad-character replacements for certain languages, like replacing `l` or `1` to [[palochka]] in some languages written in Cyrillic. (FIXME: This currently goes under the name ''display text'' but that will be repurposed below. Also, [[User:Surjection]] suggests renaming this to ''normalized input text'', but "normalized" is used in a different sense in [[Module:usex]].) # The ''display text'' is the text in the form as it will be displayed to the user. This is what appears in headwords, in usexes, in displayed internal links, etc. This can include accent marks that are removed to form the stripped display text (see below), as well as embedded bracketed links that are variously processed further. The display text is generated from the corrected input text by applying language-specific transformations; for most languages, there will be no such transformations. The general reason for having a difference between input and display text is to allow for extra information in the input text that is not displayed to the user but is sent to the transliteration module. Note that having different display and input text is only supported currently through special-casing but will be generalized. Examples of transformations are: (1) Removing the {{cd|^}} that is used in certain East Asian (and possibly other unicameral) languages to indicate capitalization of the transliteration (which is currently special-cased); (2) for Korean, removing or otherwise processing hyphens (which is currently special-cased); (3) for Arabic, removing a ''sukūn'' diacritic placed over a ''tāʔ marbūṭa'' (like this: ةْ) to indicate that the ''tāʔ marbūṭa'' is pronounced and transliterated as /t/ instead of being silent [NOTE, NOT IMPLEMENTED YET]; (4) for Thai and Khmer, converting space-separated words to bracketed words and resolving respelling substitutions such as `[กรีน/กฺรีน]`, which indicate how to transliterate given words [NOTE, NOT IMPLEMENTED YET except in language-specific templates like {{tl|th-usex}}]. ## The ''right-resolved display text'' is the result of removing brackets around one-part embedded links and resolving two-part embedded links into their right-hand components (i.e. converting two-part links into the displayed form). The process of right-resolution is what happens when you call {{cd|remove_links()}} in [[Module:links]] on some text. When applied to the display text, it produces exactly what the user sees, without any link markup. # The ''stripped display text'' is the result of applying diacritic-stripping to the display text. ## The ''left-resolved stripped display text'' [NEED BETTER NAME] is the result of applying left-resolution to the stripped display text, i.e. similar to right-resolution but resolving two-part embedded links into their left-hand components (i.e. the linked-to page). If the display text refers to a single page, the resulting of applying diacritic stripping and left-resolution produces the ''logical pagename''. # The ''physical pagename text'' is the result of converting the stripped display text into physical page links. If the stripped display text contains embedded links, the left side of those links is converted into physical page links; otherwise, the entire text is considered a pagename and converted in the same fashion. The conversion does three things: (1) converts characters not allowed in pagenames into their "unsupported title" representation, e.g. {{cd|Unsupported titles/`gt`}} in place of the logical name {{cd|>}}; (2) handles certain special-cased unsupported-title logical pagenames, such as {{cd|Unsupported titles/Space}} in place of {{cd|[space]}} and {{cd|Unsupported titles/Ancient Greek dish}} in place of a very long Greek name for a gourmet dish as found in Aristophanes; (3) converts "mammoth" pagenames such as [[a]] into their appropriate split component, e.g. [[a/languages A to L]]. # The ''source translit text'' is the text as supplied to the language-specific {{cd|transliterate()}} method. The form of the source translit text may need to be language-specific, e.g Thai and Khmer will need the corrected input text, whereas other languages may need to work off the display text. [FIXME: It's still unclear to me how embedded bracketed links are handled in the existing code.] In general, embedded links need to be right-resolved (see above), but when this happens is unclear to me [FIXME]. Some languages have a chop-up-and-paste-together scheme that sends parts of the text through the transliterate mechanism, and for others (those listed with "cont" in {{cd|substitution}} in [[Module:languages/data]]) they receive the full input text, but preprocessed in certain ways. (The wisdom of this is still unclear to me.) # The ''transliterated text'' (or ''transliteration'') is the result of transliterating the source translit text. Unlike for all the other text variants except the transcribed text, it is always in the Latin script. # The ''transcribed text'' (or ''transcription'') is the result of transcribing the source translit text, where "transcription" here means a close approximation to the phonetic form of the language in languages (e.g. Akkadian, Sumerian, Ancient Egyptian, maybe Tibetan) that have a wide difference between the written letters and spoken form. Unlike for all the other text variants other than the transliterated text, it is always in the Latin script. Currently, the transcribed text is always supplied manually be the user; there is no such thing as a {{cd|transcribe()}} method on language objects. # The ''sort key'' is the text used in sort keys for determining the placing of pages in categories they belong to. The sort key is generated from the pagename or a specified ''sort base'' by lowercasing, doing language-specific transformations and then uppercasing the result. If the sort base is supplied and is generated from input text, it needs to be converted to display text, have embedded links removed through right-resolution and have diacritic-stripping applied. # There are other text variants that occur in usexes (specifically, there are normalized variants of several of the above text variants), but we can skip them for now. The following methods exist on {Language} objects to convert between different text variants: # {correctInputText} (currently called {makeDisplayText}): This converts input text to corrected input text. # {stripDiacritics}: This converts to stripped display text. [FIXME: This needs some rethinking. In particular, {stripDiacritics} is sometimes called on input text, corrected input text or display text (in various paths inside of [[Module:links]], and, in the case of input text, usually from other modules). We need to make sure we don't try to convert input text to display text twice, but at the same time we need to support calling it directly on input text since so many modules do this. This means we need to add a parameter indicating whether the passed-in text is input, corrected input, or display text; if the former two, we call {correctInputText} ourselves.] # {logicalToPhysical}: This converts logical pagenames to physical pagenames. # {transliterate}: This appears to convert input text with embedded brackets removed into a transliteration. [FIXME: This needs some rethinking. In particular, it calls {processDisplayText} on its input, which won't work for Thai and Khmer, so we may need language-specific flags indicating whether to pass the input text directly to the language transliterate method. In addition, I'm not sure how embedded links are handled in the existing translit code; a lot of callers remove the links themselves before calling {transliterate()}, which I assume is wrong.] # {makeSortKey}: This converts display text (?) to a sort key. [FIXME: Clarify this.] ]==] local export = {} local debug_track_module = "Module:debug/track" local etymology_languages_data_module = "Module:etymology languages/data" local families_module = "Module:families" local headword_page_module = "Module:headword/page" local json_module = "Module:JSON" local language_like_module = "Module:language-like" local languages_data_module = "Module:languages/data" local languages_data_patterns_module = "Module:languages/data/patterns" local links_data_module = "Module:links/data" local load_module = "Module:load" local scripts_module = "Module:scripts" local scripts_data_module = "Module:scripts/data" local string_encode_entities_module = "Module:string/encode entities" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local wikimedia_languages_module = "Module:wikimedia languages" local mw = mw local string = string local table = table local char = string.char local concat = table.concat local find = string.find local floor = math.floor local get_by_code -- Defined below. local get_data_module_name -- Defined below. local get_extra_data_module_name -- Defined below. local getmetatable = getmetatable local gmatch = string.gmatch local gsub = string.gsub local insert = table.insert local ipairs = ipairs local is_known_language_tag = mw.language.isKnownLanguageTag local make_object -- Defined below. local match = string.match local next = next local pairs = pairs local remove = table.remove local require = require local select = select local setmetatable = setmetatable local sub = string.sub local type = type local unstrip = mw.text.unstrip -- Loaded as needed by findBestScript. local Hans_chars local Hant_chars local function check_object(...) check_object = require(utilities_module).check_object return check_object(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function get_L2_sort_key(...) get_L2_sort_key = require(headword_page_module).get_L2_sort_key return get_L2_sort_key(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function get_family(...) get_family = require(families_module).getByCode return get_family(...) end local function get_plaintext(...) get_plaintext = require(utilities_module).get_plaintext return get_plaintext(...) end local function get_wikimedia_lang(...) get_wikimedia_lang = require(wikimedia_languages_module).getByCode return get_wikimedia_lang(...) end local function keys_to_list(...) keys_to_list = require(table_module).keysToList return keys_to_list(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function make_family_object(...) make_family_object = require(families_module).makeObject return make_family_object(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function safe_require(...) safe_require = require(load_module).safe_require return safe_require(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function to_json(...) to_json = require(json_module).toJSON return to_json(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ugsub(...) ugsub = require(string_utilities_module).gsub return ugsub(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end local function track(page) debug_track("languages/" .. page) return true end local function normalize_code(code) return load_data(languages_data_module).aliases[code] or code end local function check_inputs(self, check, default, ...) local n = select("#", ...) if n == 0 then return false end local ret = check(self, (...)) if ret ~= nil then return ret elseif n > 1 then local inputs = {...} for i = 2, n do ret = check(self, inputs[i]) if ret ~= nil then return ret end end end return default end local function make_link(self, target, display) local prefix, main if self:getFamilyCode() == "qfa-sub" then prefix, main = display:match("^(the )(.*)") if not prefix then prefix, main = display:match("^(a )(.*)") end end return (prefix or "") .. "[[" .. target .. "|" .. (main or display) .. "]]" end -- Convert risky characters to HTML entities, which minimizes interference once returned (e.g. for "sms:a", "<!-- -->" etc.). local function escape_risky_characters(text) -- Spacing characters in isolation generally need to be escaped in order to be properly processed by the MediaWiki -- software. if umatch(text, "^%s*$") then return encode_entities(text, text) end return encode_entities(text, "!#%&*+/:;<=>?@[\\]_{|}") end -- Temporarily convert various formatting characters to PUA to prevent them from being disrupted by the substitution process. local function doTempSubstitutions(text, subbedChars, keepCarets, noTrim) -- Clone so that we don't insert any extra patterns into the table in package.loaded. For some reason, using require -- seems to keep memory use down; probably because the table is always cloned. local patterns = shallow_copy(require(languages_data_patterns_module)) if keepCarets then insert(patterns, "((\\+)%^)") insert(patterns, "((%^))") end -- Ensure any whitespace at the beginning and end is temp substituted, to prevent it from being accidentally -- trimmed. We only want to trim any final spaces added during the substitution process (e.g. by a module), which -- means we only do this during the first round of temp substitutions. if not noTrim then insert(patterns, "^([\128-\191\244]*(%s+))") insert(patterns, "((%s+)[\128-\191\244]*)$") end -- Pre-substitution, of "[[" and "]]", which makes pattern matching more accurate. text = gsub(text, "%f[%[]%[%[", "\1"):gsub("%f[%]]%]%]", "\2") local i = #subbedChars for _, pattern in ipairs(patterns) do -- Patterns ending in \0 stand are for things like "[[" or "]]"), so the inserted PUA are treated as breaks -- between terms by modules that scrape info from pages. local term_divider pattern = gsub(pattern, "%z$", function(divider) term_divider = divider == "\0" return "" end) text = gsub(text, pattern, function(...) local m = {...} local m1New = m[1] for k = 2, #m do local n = i + k - 1 subbedChars[n] = m[k] local byte2 = floor(n / 4096) % 64 + (term_divider and 128 or 136) local byte3 = floor(n / 64) % 64 + 128 local byte4 = n % 64 + 128 m1New = gsub(m1New, pattern_escape(m[k]), "\244" .. char(byte2) .. char(byte3) .. char(byte4), 1) end i = i + #m - 1 return m1New end) end text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]") return text, subbedChars end -- Reinsert any formatting that was temporarily substituted. local function undoTempSubstitutions(text, subbedChars) for i = 1, #subbedChars do local byte2 = floor(i / 4096) % 64 + 128 local byte3 = floor(i / 64) % 64 + 128 local byte4 = i % 64 + 128 text = gsub(text, "\244[" .. char(byte2) .. char(byte2+8) .. "]" .. char(byte3) .. char(byte4), replacement_escape(subbedChars[i])) end text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]") return text end -- Check if the raw text is an unsupported title, and if so return that. Otherwise, remove HTML entities. We do the -- pre-conversion to avoid loading the unsupported title list unnecessarily. local function checkNoEntities(text) local textNoEnc = decode_entities(text) if textNoEnc ~= text and load_data(links_data_module).unsupported_titles[text] then return text else return textNoEnc end end -- If no script object is provided (or if it's invalid or None), get one. local function checkScript(text, self, sc) if not check_object("script", true, sc) or sc:getCode() == "None" then return self:findBestScript(text) end return sc end local function normalize(text, sc) text = sc:fixDiscouragedSequences(text) return sc:toFixedNFD(text) end --[=[ Subfunction of iterateSectionSubstitutions(). Process an individual chunk of text according to the specifications in `substitution_data`. The input parameters are all as in the documentation of iterateSectionSubstitutions() except for `recursed`, which is set to true if we called ourselves recursively to process a script-specific setting or script-wide fallback. Returns two values: the processed text and the actual substitution data used to do the substitutions (same as the `actual_substitution_data` return value to iterateSectionSubstitutions()). ]=] local function doSubstitutions(self, text, sc, substitution_data, data_field, function_name, recursed) -- BE CAREFUL in this function because the value at any level can be `false`, which causes no processing to be done -- and blocks any further fallback processing. local actual_substitution_data = substitution_data -- If there are language-specific substitutes given in the data module, use those. if type(substitution_data) == "table" then -- If a script is specified, run this function with the script-specific data before continuing. local sc_code = sc:getCode() local has_substitution_data = false if substitution_data[sc_code] ~= nil then has_substitution_data = true if substitution_data[sc_code] then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[sc_code], data_field, function_name, true) end -- Hant, Hans and Hani are usually treated the same, so add a special case to avoid having to specify each one -- separately. elseif sc_code:match("^Han") and substitution_data.Hani ~= nil then has_substitution_data = true if substitution_data.Hani then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data.Hani, data_field, function_name, true) end -- Substitution data with key 1 in the outer table may be given as a fallback. elseif substitution_data[1] ~= nil then has_substitution_data = true if substitution_data[1] then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[1], data_field, function_name, true) end end -- Iterate over all strings in the "from" subtable, and gsub with the corresponding string in "to". We work with -- the NFD decomposed forms, as this simplifies many substitutions. if substitution_data.from then has_substitution_data = true for i, from in ipairs(substitution_data.from) do -- Normalize each loop, to ensure multi-stage substitutions work correctly. text = sc:toFixedNFD(text) text = ugsub(text, sc:toFixedNFD(from), substitution_data.to[i] or "") end end if substitution_data.remove_diacritics then has_substitution_data = true text = sc:toFixedNFD(text) -- Convert exceptions to PUA. local remove_exceptions, substitutes = substitution_data.remove_exceptions if remove_exceptions then substitutes = {} local i = 0 for _, exception in ipairs(remove_exceptions) do exception = sc:toFixedNFD(exception) text = ugsub(text, exception, function(m) i = i + 1 local subst = u(0x80000 + i) substitutes[subst] = m return subst end) end end -- Strip diacritics. text = ugsub(text, "[" .. substitution_data.remove_diacritics .. "]", "") -- Convert exceptions back. if remove_exceptions then text = text:gsub("\242[\128-\191]*", substitutes) end end if not has_substitution_data and sc._data[data_field] then -- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.). text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field, function_name, true) end elseif type(substitution_data) == "string" then -- If there is a dedicated function module, use that. local module = safe_require("Module:" .. substitution_data) if module then -- TODO: translit functions should take objects, not codes. -- TODO: translit functions should be called with form NFD. if function_name == "tr" then if not module[function_name] then error(("Internal error: Module [[%s]] has no function named 'tr'"):format(substitution_data)) end text = module[function_name](text, self._code, sc:getCode()) elseif function_name == "stripDiacritics" then -- FIXME, get rid of this arm after renaming makeEntryName -> stripDiacritics. if module[function_name] then text = module[function_name](sc:toFixedNFD(text), self, sc) elseif module.makeEntryName then text = module.makeEntryName(sc:toFixedNFD(text), self, sc) else error(("Internal error: Module [[%s]] has no function named 'stripDiacritics' or 'makeEntryName'" ):format(substitution_data)) end else if not module[function_name] then error(("Internal error: Module [[%s]] has no function named '%s'"):format( substitution_data, function_name)) end text = module[function_name](sc:toFixedNFD(text), self, sc) end else error("Substitution data '" .. substitution_data .. "' does not match an existing module.") end elseif substitution_data == nil and sc._data[data_field] then -- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.). text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field, function_name, true) end -- Don't normalize to NFC if this is the inner loop or if a module returned nil. if recursed or not text then return text, actual_substitution_data end -- Fix any discouraged sequences created during the substitution process, and normalize into the final form. return sc:toFixedNFC(sc:fixDiscouragedSequences(text)), actual_substitution_data end --[=[ Split the text into sections, based on the presence of temporarily substituted formatting characters, then iterate over each section to apply substitutions (e.g. transliteration or diacritic stripping). This avoids putting PUA (Private Use Area) characters through language-specific modules, which may be unequipped for them. This function is passed the following values: * `self` (the Language object); * `text` (the text to process); * `sc` (the script of the text, which must be specified; callers should call checkScript() as needed to autodetect the script of the text if not given explicitly by the user); * `subbedChars` (an array of the same length as the text, indicating which characters have been substituted and by what, or {nil} if no substitutions are to happen); * `keepCarets` (DOCUMENT ME); * `substitution_data` (the data indicating which substitutions to apply, taken directly from `data_field` in the language's data structure in a submodule of [[Module:languages/data]]); * `data_field` (the field from which `substitution_data` was fetched, such as {"sort_key"} or {"strip_diacritics"}); * `function_name` (the name of the function to call to do the substitution, in case `substitution_data` specifies a module to do the substitution); * `notrim` (don't trim whitespace at the edges of `text`; set when computing the sort key, because whitespace at the beginning of a sort key is significant and causes the resulting page to be sorted at the beginning of the category it's in). Return three values: # the processed text; # the value of `subbedChars` that was passed in, possibly modified with additional character substitutions; will be {nil} if {nil} was passed in; # the actual substitution data that was used to apply substitutions to `text`; this may be different from the value of `substitution_data` passed in if that value recursively specified script-specific substitutions or if no substitution data could be found in the language-specific data (e.g. {nil} was passed in or a structure was passed in that had no setting for the script given in `sc`), but a script-wide fallback value was set; currently it is only used by {makeSortKey()}. ]=] local function iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, substitution_data, data_field, function_name, notrim) local sections -- See [[Module:languages/data]]. if not find(text, "\244") or load_data(languages_data_module).substitution[self._code] == "cont" then sections = {text} else sections = split(text, "\244[\128-\143][\128-\191]*", true) end local actual_substitution_data for _, section in ipairs(sections) do -- Don't bother processing empty strings or whitespace (which may also not be handled well by dedicated -- modules). if gsub(section, "%s+", "") ~= "" then local sub, this_actual_substitution_data = doSubstitutions(self, section, sc, substitution_data, data_field, function_name) actual_substitution_data = this_actual_substitution_data -- Second round of temporary substitutions, in case any formatting was added by the main substitution -- process. However, don't do this if the section contains formatting already (as it would have had to have -- been escaped to reach this stage, and therefore should be given as raw text). if sub and subbedChars then local noSub for _, pattern in ipairs(require(languages_data_patterns_module)) do if match(section, pattern .. "%z?") then noSub = true end end if not noSub then sub, subbedChars = doTempSubstitutions(sub, subbedChars, keepCarets, true) end end if not sub then text = sub break end text = sub and gsub(text, pattern_escape(section), replacement_escape(sub), 1) or text end end if not notrim then -- Trim, unless there are only spacing characters, while ignoring any final formatting characters. -- Do not trim sort keys because spaces at the beginning are significant. text = text and text:gsub("^([\128-\191\244]*)%s+(%S)", "%1%2"):gsub("(%S)%s+([\128-\191\244]*)$", "%1%2") or nil end return text, subbedChars, actual_substitution_data end -- Process carets (and any escapes). Default to simple removal, if no pattern/replacement is given. local function processCarets(text, pattern, repl) local rep repeat text, rep = gsub(text, "\\\\(\\*^)", "\3%1") until rep == 0 return (text:gsub("\\^", "\4") :gsub(pattern or "%^", repl or "") :gsub("\3", "\\") :gsub("\4", "^")) end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). local function removeCarets(text, sc) if not sc:hasCapitalization() and sc:isTransliterated() and text:find("^", 1, true) then return processCarets(text) else return text end end local Language = {} --[==[ Return the language code of the language. Example: {fr"} for French. ]==] function Language:getCode() return self._code end --[==[ Return the canonical name of the language. This is the name used to represent that language on Wiktionary, and is guaranteed to be unique to that language alone. Example: {"French"} for French. ]==] function Language:getCanonicalName() local name = self._name if name == nil then name = self._data[1] self._name = name end return name end --[==[ Return the display form of the language. The display form of a language, family or script is the form it takes when appearing as the <code><var>source</var></code> in categories such as <code>English terms derived from <var>source</var></code> or <code>English given names from <var>source</var></code>, and is also the displayed text in `##makeCategoryLink()` links. For full and etymology-only languages, this is the same as the canonical name, but for families, it reads <code>"<var>name</var> languages"</code> (e.g. {"Indo-Iranian languages"}), and for scripts, it reads <code>"<var>name</var> script"</code> (e.g. {"Arabic script"}). ]==] function Language:getDisplayForm() local form = self._displayForm if form == nil then form = self:getCanonicalName() -- Add article and " substrate" to substrates that lack them. if self:getFamilyCode() == "qfa-sub" then if not (sub(form, 1, 4) == "the " or sub(form, 1, 2) == "a ") then form = "a " .. form end if not match(form, " [Ss]ubstrate") then form = form .. " substrate" end end self._displayForm = form end return form end --[==[ Return the value which should be used in the HTML `lang=` attribute for tagged text in the language. ]==] function Language:getHTMLAttribute(sc, region) local code = self._code if not find(code, "-", 1, true) then return code .. "-" .. sc:getCode() .. (region and "-" .. region or "") end local parent = self:getParent() region = region or match(code, "%f[%u][%u-]+%f[%U]") if parent then return parent:getHTMLAttribute(sc, region) end -- TODO: ISO family codes can also be used. return "mis-" .. sc:getCode() .. (region and "-" .. region or "") end --[==[ Return a list of the aliases that the language is known by, excluding the canonical name. Aliases are synonyms for the language in question. The names are not guaranteed to be unique, in that sometimes more than one language is known by the same name. Example: { {"High German", "New High German", "Deutsch"}} for {{lnl|de}}. ]==] function Language:getAliases() self:loadInExtraData() return require(language_like_module).getAliases(self) end --[==[ Return a list of the known subvarieties of a given language, excluding subvarieties that have been given explicit etymology-only language codes. The names are not guaranteed to be unique, in that sometimes a given name refers to a subvariety of more than one language. Example: { {"Southern Aymara", "Central Aymara"}} for {{lnl|ay}}. Note that the returned value can have nested tables in it, when a subvariety goes by more than one name. Example: { {"North Azerbaijani", "South Azerbaijani", {"Afshar", "Afshari", "Afshar Azerbaijani", "Afchar"}, {"Qashqa'i", "Qashqai", "Kashkay"}, "Sonqor"}} for {{lnl|az}}. Here, for example, Afshar, Afshari, Afshar Azerbaijani and Afchar all refer to the same subvariety, whose preferred name is Afshar (the one listed first). To avoid a return value with nested tables in it, specify a non-{nil} value for the `flatten` parameter; in that case, the return value would be { {"North Azerbaijani", "South Azerbaijani", "Afshar", "Afshari", "Afshar Azerbaijani", "Afchar", "Qashqa'i", "Qashqai", "Kashkay", "Sonqor"}}. ]==] function Language:getVarieties(flatten) self:loadInExtraData() return require(language_like_module).getVarieties(self, flatten) end --[==[ Return a table of the "other names" that the language is known by, which are listed in the `other_names` field in the language's extra-data module. It should be noted that the `other_names` field itself is deprecated, and entries listed there should eventually be moved to either `aliases` or `varieties`, or removed if they refer to larger entities which the language in question is actually a part of. ]==] function Language:getOtherNames() -- To be eventually removed, once there are no more uses of the `other_names` field. self:loadInExtraData() return require(language_like_module).getOtherNames(self) end --[==[ Return a combined table of the canonical name, aliases, varieties and other names of a given language. ]==] function Language:getAllNames() self:loadInExtraData() return require(language_like_module).getAllNames(self) end --[==[ Return a table of types as a set (with the types as keys). The possible types are * {language}: This is a language, either full or etymology-only. * {full}: This is a "full" (not etymology-only) language, i.e. the union of {regular}, {reconstructed} and {appendix-constructed}. Note that the types {full} and {etymology-only} also exist for families, so if you want to check specifically for a full language and you have an object that might be a family, you should use {hasType("language", "full")} and not simply {hasType("full")}. * {etymology-only}: This is an etymology-only (not full) language, whose parent is another etymology-only language or a full language. Note that the types {full} and {etymology-only} also exist for families, so if you want to check specifically for an etymology-only language and you have an object that might be a family, you should use {hasType("language", "etymology-only")} and not simply {hasType("etymology-only")}. * {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted in the main namespace. There may also be reconstructed terms for the language, which are placed in the {Reconstruction} namespace and must be prefixed with `*` to indicate a reconstruction. Most full languages are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto and Volapük, among others) are also allowed in the mainspace and considered regular languages. * {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the {Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed with `*`. Languages such as Proto-Indo-European and Proto-Germanic are in this category. '''Exception:''' Reconstructed languages can have entries in the mainspace using the ''anti-asterisk'' feature. This requires that both the headword for the mainspace term (as specified using {{para|head}}) and links to the term are prefixed with the anti-asterisk indicator `!!`. * {appendix-constructed}: This language is attested but does not meet the additional requirements set out for constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore be in the Appendix namespace, but they are not reconstructed and therefore should not have * prefixed in links. ]==] function Language:getTypes() local types = self._types if types == nil then types = {language = true} if self:getFullCode() == self._code then types.full = true else types["etymology-only"] = true end for t in gmatch(self._data.type, "[^,]+") do types[t] = true end self._types = types end return types end --[==[ Given a list of types as strings, return {true} if the language has all of them. ]==] function Language:hasType(...) Language.hasType = require(language_like_module).hasType return self:hasType(...) end --[==[ Return a list containing `WikimediaLanguage` objects (see [[Module:wikimedia languages]]), which represent languages and their codes as they are used in Wikimedia projects for interwiki linking and such. More than one object may be returned, as a single Wiktionary language may correspond to multiple Wikimedia languages. For example, Wiktionary's single code `sh` (Serbo-Croatian) maps to four Wikimedia codes: `sh` (Serbo-Croatian), `bs` (Bosnian), `hr` (Croatian) and `sr` (Serbian). The code for the Wikimedia language is retrieved from the `wikimedia_codes` property in the data modules. If that property is not present, the code of the current language is used. If none of the available codes is actually a valid Wikimedia code, an empty list is returned. ]==] function Language:getWikimediaLanguages() local wm_langs = self._wikimediaLanguageObjects if wm_langs == nil then local codes = self:getWikimediaLanguageCodes() wm_langs = {} for i = 1, #codes do wm_langs[i] = get_wikimedia_lang(codes[i]) end self._wikimediaLanguageObjects = wm_langs end return wm_langs end function Language:getWikimediaLanguageCodes() local wm_langs = self._wikimediaLanguageCodes if wm_langs == nil then wm_langs = self._data.wikimedia_codes if wm_langs then wm_langs = split(wm_langs, ",", true, true) else local code = self._code if is_known_language_tag(code) then wm_langs = {code} else -- Inherit, but only if no codes are specified in the data *and* -- the language code isn't a valid Wikimedia language code. local parent = self:getParent() wm_langs = parent and parent:getWikimediaLanguageCodes() or {} end end self._wikimediaLanguageCodes = wm_langs end return wm_langs end --[==[ Return the name of the Wikipedia article for the language. `project` specifies the language and project to retrieve the article from, defaulting to {"enwiki"} for the English Wikipedia. Normally if specified it should be the project code for a specific-language Wikipedia e.g. "zhwiki" for the Chinese Wikipedia, but it can be any project, including non-Wikipedia ones. If the project is the English Wikipedia and the property {wikipedia_article} is present in the data module it will be used first. In all other cases, a sitelink will be generated from {:getWikidataItem} (if set). The resulting value (or lack of value) is cached so that subsequent calls are fast. If no value could be determined, and `noCategoryFallback` is {false}, `##Language:getCategoryName` is used as fallback; otherwise, {nil} is returned. Note that if `noCategoryFallback` is {nil} or omitted, it defaults to {false} if the project is the English Wikipedia, otherwise to {true}. In other words, under normal circumstances, if the English Wikipedia article couldn't be retrieved, the return value will fall back to a link to the language's category, but this won't normally happen for any other project. ]==] function Language:getWikipediaArticle(noCategoryFallback, project) Language.getWikipediaArticle = require(language_like_module).getWikipediaArticle return self:getWikipediaArticle(noCategoryFallback, project) end function Language:makeWikipediaLink() return make_link(self, "w:" .. self:getWikipediaArticle(), self:getCanonicalName()) end --[==[ Return the name of the Wikimedia Commons category page for the language. ]==] function Language:getCommonsCategory() Language.getCommonsCategory = require(language_like_module).getCommonsCategory return self:getCommonsCategory() end --[==[ Return the Wikidata item ID for the language or {nil}. This corresponds to the the second field in the data modules. ]==] function Language:getWikidataItem() Language.getWikidataItem = require(language_like_module).getWikidataItem return self:getWikidataItem() end --[==[ Return a list of `Script` objects for all scripts that the language is written in. See [[Module:scripts]]. ]==] function Language:getScripts() local scripts = self._scriptObjects if scripts == nil then local codes = self:getScriptCodes() if codes[1] == "All" then scripts = load_data(scripts_data_module) else scripts = {} for i = 1, #codes do scripts[i] = get_script(codes[i]) end end self._scriptObjects = scripts end return scripts end --[==[ Return the list of script codes in the language's data file. ]==] function Language:getScriptCodes() local scripts = self._scriptCodes if scripts == nil then scripts = self._data[4] if scripts then local codes, n = {}, 0 for code in gmatch(scripts, "[^,]+") do n = n + 1 -- Special handling of "Hants", which represents "Hani", "Hant" and "Hans" collectively. if code == "Hants" then codes[n] = "Hani" codes[n + 1] = "Hant" codes[n + 2] = "Hans" n = n + 2 else codes[n] = code end end scripts = codes else scripts = {"None"} end self._scriptCodes = scripts end return scripts end --[==[ Given some text, iterate through the scripts of a given language trying to find the script that best matches the text. Return a {Script} object representing the script. If no match is found at all, it returns the {None} script object. ]==] function Language:findBestScript(text, forceDetect) if not text or text == "" or text == "-" then return get_script("None") end -- Differs from table returned by getScriptCodes, as Hants is not normalized into its constituents. local codes = self._bestScriptCodes if codes == nil then codes = self._data[4] codes = codes and split(codes, ",", true, true) or {"None"} self._bestScriptCodes = codes end local first_sc = codes[1] if first_sc == "All" then return find_best_script_without_lang(text) end local codes_len = #codes if not (forceDetect or first_sc == "Hants" or codes_len > 1) then first_sc = get_script(first_sc) local charset = first_sc.characters return charset and umatch(text, "[" .. charset .. "]") and first_sc or get_script("None") end -- Remove all formatting characters. text = get_plaintext(text) -- Remove all spaces and any ASCII punctuation. Some non-ASCII punctuation is script-specific, so can't be removed. text = ugsub(text, "[%s!\"#%%&'()*,%-./:;?@[\\%]_{}]+", "") if #text == 0 then return get_script("None") end -- Try to match every script against the text, -- and return the one with the most matching characters. local bestcount, bestscript, length = 0 for i = 1, codes_len do local sc = codes[i] -- Special case for "Hants", which is a special code that represents whichever of "Hant" or "Hans" best matches, -- or "Hani" if they match equally. This avoids having to list all three. In addition, "Hants" will be treated -- as the best match if there is at least one matching character, under the assumption that a Han script is -- desirable in terms that contain a mix of Han and other scripts (not counting those which use Jpan or Kore). if sc == "Hants" then local Hani = get_script("Hani") if not Hant_chars then Hant_chars = load_data("Module:zh/data/ts") Hans_chars = load_data("Module:zh/data/st") end local t, s, found = 0, 0 -- This is faster than using mw.ustring.gmatch directly. for ch in gmatch((ugsub(text, "[" .. Hani.characters .. "]", "\255%0")), "\255(.[\128-\191]*)") do found = true if Hant_chars[ch] then t = t + 1 if Hans_chars[ch] then s = s + 1 end elseif Hans_chars[ch] then s = s + 1 else t, s = t + 1, s + 1 end end if found then if t == s then return Hani end return get_script(t > s and "Hant" or "Hans") end else sc = get_script(sc) if not length then length = ulen(text) end -- Count characters by removing everything in the script's charset and comparing to the original length. local charset = sc.characters local count = charset and length - ulen((ugsub(text, "[" .. charset .. "]+", ""))) or 0 if count >= length then return sc elseif count > bestcount then bestcount = count bestscript = sc end end end -- Return best matching script, or otherwise None. return bestscript or get_script("None") end --[==[ Return a `Family` object for the language family that the language belongs to. See [[Module:families]]. ]==] function Language:getFamily() local family = self._familyObject if family == nil then family = self:getFamilyCode() -- If the value is nil, it's cached as false. family = family and get_family(family) or false self._familyObject = family end return family or nil end --[==[ Return the family code in the language's data file. ]==] function Language:getFamilyCode() local family = self._familyCode if family == nil then -- If the value is nil, it's cached as false. family = self._data[3] or false self._familyCode = family end return family or nil end function Language:getFamilyName() local family = self._familyName if family == nil then family = self:getFamily() -- If the value is nil, it's cached as false. family = family and family:getCanonicalName() or false self._familyName = family end return family or nil end do local function check_family(self, family) if type(family) == "table" then family = family:getCode() end if self:getFamilyCode() == family then return true end local self_family = self:getFamily() if self_family:inFamily(family) then return true -- If the family isn't a real family (e.g. creoles) check any ancestors. elseif self_family:inFamily("qfa-not") then local ancestors = self:getAncestors() for _, ancestor in ipairs(ancestors) do if ancestor:inFamily(family) then return true end end end end --[==[ Check whether the language belongs to `family` (which can be a family code or object). A list of objects can be given in place of `family`; in that case, return true if the language belongs to any of the specified families. Note that some languages (in particular, certain creoles) can have multiple immediate ancestors potentially belonging to different families; in that case, return true if the language belongs to any of the specified families. ]==] function Language:inFamily(...) if self:getFamilyCode() == nil then return false end return check_inputs(self, check_family, false, ...) end end function Language:getParent() local parent = self._parentObject if parent == nil then parent = self:getParentCode() -- If the value is nil, it's cached as false. parent = parent and get_by_code(parent, nil, true, true) or false self._parentObject = parent end return parent or nil end function Language:getParentCode() local parent = self._parentCode if parent == nil then -- If the value is nil, it's cached as false. parent = self._data.parent or false self._parentCode = parent end return parent or nil end function Language:getParentName() local parent = self._parentName if parent == nil then parent = self:getParent() -- If the value is nil, it's cached as false. parent = parent and parent:getCanonicalName() or false self._parentName = parent end return parent or nil end function Language:getParentChain() local chain = self._parentChain if chain == nil then chain = {} local parent, n = self:getParent(), 0 while parent do n = n + 1 chain[n] = parent parent = parent:getParent() end self._parentChain = chain end return chain end --[==[ Return a list of the pseudo-families (if any) that a language belongs to. The returned value is a list of Family objects. Pseudo-families are groupings of languages that are not clades but share some geographic, ethnic or other characteristic (e.g. [[:Category:Papuan languages]], [[:Category:Jewish languages]]). ]==] function Language:getPseudoFamilies() self:loadInExtraData() return require(language_like_module).getPseudoFamilies(self) end do local function check_lang(self, lang) for _, parent in ipairs(self:getParentChain()) do if (type(lang) == "string" and lang or lang:getCode()) == parent:getCode() then return true end end end function Language:hasParent(...) return check_inputs(self, check_lang, false, ...) end end --[==[ If the language is etymology-only, iterate through parents until a full language or family is found, and the corresponding object is returned. If the language is a full language, return that language. ]==] function Language:getFull() local full = self._fullObject if full == nil then full = self:getFullCode() full = full == self._code and self or get_by_code(full) self._fullObject = full end return full end --[==[ If the language is an etymology-only language, iterate through parents until a full language or family is found, and the corresponding code is returned. If the language is a full language, return that language's code. ]==] function Language:getFullCode() return self._fullCode or self._code end --[==[ If the language is an etymology-only language, iterate through parents until a full language or family is found, and the corresponding canonical name is returned. If the language is a full language, return that language's canonical name. ]==] function Language:getFullName() local full = self._fullName if full == nil then full = self:getFull():getCanonicalName() self._fullName = full end return full end --[==[ Return a list of {Language} objects for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors. ]==] function Language:getAncestors() local ancestors = self._ancestorObjects if ancestors == nil then ancestors = {} local ancestor_codes = self:getAncestorCodes() if #ancestor_codes > 0 then for _, ancestor in ipairs(ancestor_codes) do insert(ancestors, get_by_code(ancestor, nil, true)) end else local fam = self:getFamily() local protoLang = fam and fam:getProtoLanguage() or nil -- For the cases where the current language is the proto-language -- of its family, or an etymology-only language that is ancestral to that -- proto-language, we need to step up a level higher right from the -- start. if protoLang and ( protoLang:getCode() == self._code or (self:hasType("etymology-only") and protoLang:hasAncestor(self)) ) then fam = fam:getFamily() protoLang = fam and fam:getProtoLanguage() or nil end while not protoLang and not (not fam or fam:getCode() == "qfa-not") do fam = fam:getFamily() protoLang = fam and fam:getProtoLanguage() or nil end insert(ancestors, protoLang) end self._ancestorObjects = ancestors end return ancestors end do -- Avoid a language being its own ancestor via class inheritance. We only need to check for this if the language has -- inherited an ancestor table from its parent, because we never want to drop ancestors that have been explicitly -- set in the data. Recursively iterate over ancestors until we either find self or run out. If self is found, -- return true. local function check_ancestor(self, lang) local codes = lang:getAncestorCodes() if not codes then return nil end for i = 1, #codes do local code = codes[i] if code == self._code then return true end local anc = get_by_code(code, nil, true) if check_ancestor(self, anc) then return true end end end --[==[ Return a list of `Language` codes for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors. ]==] function Language:getAncestorCodes() if self._ancestorCodes then return self._ancestorCodes end local data = self._data local codes = data.ancestors if codes == nil then codes = {} self._ancestorCodes = codes return codes end codes = split(codes, ",", true, true) self._ancestorCodes = codes -- If there are no codes or the ancestors weren't inherited data, there's nothing left to check. if #codes == 0 or self:getData(false, "raw").ancestors ~= nil then return codes end local i = 1 while i <= #codes do if check_ancestor(self, self) then remove(codes, i) else i = i + 1 end end return codes end end --[==[ Given a list of language objects or codes, return true if at least one of them is an ancestor. This includes any etymology-only children of that ancestor. If the language's ancestor(s) are etymology-only languages, it will also return true for those language parent(s) (e.g. if Vulgar Latin is the ancestor, it will also return true for its parent, Latin). However, a parent is excluded from this if the ancestor is also ancestral to that parent (e.g. if Classical Persian is the ancestor, Persian would return {false}, because Classical Persian is also ancestral to Persian). ]==] function Language:hasAncestor(...) local function iterateOverAncestorTree(node, func, parent_check) local ancestors = node:getAncestors() local ancestorsParents = {} for _, ancestor in ipairs(ancestors) do -- When checking the parents of the other language, and the ancestor is also a parent, skip to the next -- ancestor, so that we exclude any etymology-only children of that parent that are not directly related -- (see below). local ret = (parent_check or not node:hasParent(ancestor)) and func(ancestor) or iterateOverAncestorTree(ancestor, func, parent_check) if ret then return ret end end -- Check the parents of any ancestors. We don't do this if checking the parents of the other language, so that -- we exclude any etymology-only children of those parents that are not directly related (e.g. if the ancestor -- is Vulgar Latin and we are checking New Latin, we want it to return false because they are on different -- ancestral branches. As such, if we're already checking the parent of New Latin (Latin) we don't want to -- compare it to the parent of the ancestor (Latin), as this would be a false positive; it should be one or the -- other). if not parent_check then return nil end for _, ancestor in ipairs(ancestors) do local ancestorParents = ancestor:getParentChain() for _, ancestorParent in ipairs(ancestorParents) do if ancestorParent:getCode() == self._code or ancestorParent:hasAncestor(ancestor) then break else insert(ancestorsParents, ancestorParent) end end end for _, ancestorParent in ipairs(ancestorsParents) do local ret = func(ancestorParent) if ret then return ret end end end local function do_iteration(otherlang, parent_check) -- otherlang can't be self if (type(otherlang) == "string" and otherlang or otherlang:getCode()) == self._code then return false end repeat if iterateOverAncestorTree( self, function(ancestor) return ancestor:getCode() == (type(otherlang) == "string" and otherlang or otherlang:getCode()) end, parent_check ) then return true elseif type(otherlang) == "string" then otherlang = get_by_code(otherlang, nil, true) end otherlang = otherlang:getParent() parent_check = false until not otherlang end local parent_check = true for _, otherlang in ipairs{...} do local ret = do_iteration(otherlang, parent_check) if ret then return true end end return false end do local function construct_node(lang, memo) local branch, ancestors = {lang = lang:getCode()} memo[lang:getCode()] = branch for _, ancestor in ipairs(lang:getAncestors()) do if ancestors == nil then ancestors = {} end insert(ancestors, memo[ancestor:getCode()] or construct_node(ancestor, memo)) end branch.ancestors = ancestors return branch end function Language:getAncestorChain() local chain = self._ancestorChain if chain == nil then chain = construct_node(self, {}) self._ancestorChain = chain end return chain end end function Language:getAncestorChainOld() local chain = self._ancestorChain if chain == nil then chain = {} local step = self while true do local ancestors = step:getAncestors() step = #ancestors == 1 and ancestors[1] or nil if not step then break end insert(chain, step) end self._ancestorChain = chain end return chain end local function fetch_descendants(self, fmt) local descendants, family = {}, self:getFamily() -- Iterate over all three datasets. for _, data in ipairs{ require("Module:languages/code to canonical name"), require("Module:etymology languages/code to canonical name"), require("Module:families/code to canonical name"), } do for code in pairs(data) do local lang = get_by_code(code, nil, true, true) if not lang then error(("Internal error: code '%s' can't be fetched"):format(code)) end -- Test for a descendant. Earlier tests weed out most candidates, while the more intensive tests are only used sparingly. if ( code ~= self._code and -- Not self. lang:inFamily(family) and -- In the same family. ( family:getProtoLanguageCode() == self._code or -- Self is the protolanguage. self:hasDescendant(lang) or -- Full hasDescendant check. (lang:getFullCode() == self._code and not self:hasAncestor(lang)) -- Etymology-only child which isn't an ancestor. ) ) then if fmt == "object" then insert(descendants, lang) elseif fmt == "code" then insert(descendants, code) elseif fmt == "name" then insert(descendants, lang:getCanonicalName()) end end end end return descendants end function Language:getDescendants() local descendants = self._descendantObjects if descendants == nil then descendants = fetch_descendants(self, "object") self._descendantObjects = descendants end return descendants end function Language:getDescendantCodes() local descendants = self._descendantCodes if descendants == nil then descendants = fetch_descendants(self, "code") self._descendantCodes = descendants end return descendants end function Language:getDescendantNames() local descendants = self._descendantNames if descendants == nil then descendants = fetch_descendants(self, "name") self._descendantNames = descendants end return descendants end do local function check_lang(self, lang) if type(lang) == "string" then lang = get_by_code(lang, nil, true) end if lang:hasAncestor(self) then return true end end function Language:hasDescendant(...) return check_inputs(self, check_lang, false, ...) end end local function fetch_children(self, fmt) local m_etym_data = require(etymology_languages_data_module) local self_code, children = self._code, {} for code, lang in pairs(m_etym_data) do local _lang = lang repeat local parent = _lang.parent if parent == self_code then if fmt == "object" then insert(children, get_by_code(code, nil, true)) elseif fmt == "code" then insert(children, code) elseif fmt == "name" then insert(children, lang[1]) end break end _lang = m_etym_data[parent] until not _lang end return children end function Language:getChildren() local children = self._childObjects if children == nil then children = fetch_children(self, "object") self._childObjects = children end return children end function Language:getChildrenCodes() local children = self._childCodes if children == nil then children = fetch_children(self, "code") self._childCodes = children end return children end function Language:getChildrenNames() local children = self._childNames if children == nil then children = fetch_children(self, "name") self._childNames = children end return children end function Language:hasChild(...) local lang = ... if not lang then return false elseif type(lang) == "string" then lang = get_by_code(lang, nil, true) end if lang:hasParent(self) then return true end return self:hasChild(select(2, ...)) end --[==[ Return the name of the main category of that language. Example: {French language"} for French, whose category is at [[:Category:French language]]. Unless optional argument `nocap` is given, the language name at the beginning of the returned value will be capitalized. This capitalization is correct for category names, but not if the language name is lowercase and the returned value of this function is used in the middle of a sentence. ]==] function Language:getCategoryName(nocap) local name = self._categoryName if name == nil then name = self:getCanonicalName() -- If a substrate, omit any leading article. if self:getFamilyCode() == "qfa-sub" then name = name:gsub("^the ", ""):gsub("^a ", "") end -- Only add " language" if a full language. if self:hasType("full") then -- Unless the canonical name already ends with "language", "lect" or their derivatives, add " language". if not (match(name, "[Ll]anguage$") or match(name, "[Ll]ect$")) then name = name .. " language" end end self._categoryName = name end if nocap then return name end return mw.getContentLanguage():ucfirst(name) end --[==[ Create a link to the category; the link text is the canonical name. ]==] function Language:makeCategoryLink() return make_link(self, ":Category:" .. self:getCategoryName(), self:getDisplayForm()) end function Language:getStandardCharacters(sc) local standard_chars = self._data.standard_chars if type(standard_chars) ~= "table" then return standard_chars elseif sc and type(sc) ~= "string" then check_object("script", nil, sc) sc = sc:getCode() end if (not sc) or sc == "None" then local scripts = {} for _, script in pairs(standard_chars) do insert(scripts, script) end return concat(scripts) end if standard_chars[sc] then return standard_chars[sc] .. (standard_chars[1] or "") end end --[==[ Strip diacritics from display text `text` (in a language-specific fashion), which is in the script `sc`. If `sc` is omitted or {nil}, the script is autodetected. This also strips certain punctuation characters from the end and (in the case of Spanish upside-down question mark and exclamation points) from the beginning; strips any whitespace at the end of the text or between the text and final stripped punctuation characters; and applies some language-specific Unicode normalizations to replace discouraged characters with their prescribed alternatives. Return the stripped text. ]==] function Language:stripDiacritics(text, sc) if (not text) or text == "" then return text end sc = checkScript(text, self, sc) text = normalize(text, sc) -- FIXME, rename makeEntryName to stripDiacritics and get rid of second and third return values -- everywhere local _ text, _, _ = iterateSectionSubstitutions(self, text, sc, nil, nil, self._data.strip_diacritics or self._data.entry_name, "strip_diacritics", "stripDiacritics") text = umatch(text, "^[¿¡]?(.-[^%s%p].-)%s*[؟?!;՛՜ ՞ ՟?!︖︕।॥။၊་།]?$") or text return text end --[==[ Convert a ''logical'' pagename (the pagename as it appears to the user, after diacritics and punctuation have been stripped) to a ''physical'' pagename (the pagename as it appears in the MediaWiki database). Reasons for a difference between the two are (a) unsupported titles such as `[ ]` (with square brackets in them), `#` (pound/hash sign) and `¯\_(ツ)_/¯` (with underscores), as well as overly long titles of various sorts; (b) "mammoth" pages that are split into parts (e.g. `a`, which is split into physical pagenames `a/languages A to L` and `a/languages M to Z`). For almost all purposes, you should work with logical and not physical pagenames. But there are certain use cases that require physical pagenames, such as checking the existence of a page or retrieving a page's contents. `pagename` is the logical pagename to be converted. `is_reconstructed_or_appendix` indicates whether the page is in the `Reconstruction` or `Appendix` namespaces. If it is omitted or has the value {nil}, the pagename is checked for an initial asterisk, and if found, the page is assumed to be a `Reconstruction` page. Setting a value of `false` or `true` to `is_reconstructed_or_appendix` disables this check and allows for mainspace pagenames that begin with an asterisk. ]==] function Language:logicalToPhysical(pagename, is_reconstructed_or_appendix) -- FIXME: This probably shouldn't happen but it happens when makeEntryName() receives nil. if pagename == nil then track("nil-passed-to-logicalToPhysical") return nil end local initial_asterisk if is_reconstructed_or_appendix == nil then local pagename_minus_initial_asterisk initial_asterisk, pagename_minus_initial_asterisk = pagename:match("^(%*)(.*)$") if pagename_minus_initial_asterisk then is_reconstructed_or_appendix = true pagename = pagename_minus_initial_asterisk elseif self:hasType("appendix-constructed") then is_reconstructed_or_appendix = true end end if not is_reconstructed_or_appendix then -- Check if the pagename is a listed unsupported title. local unsupportedTitles = load_data(links_data_module).unsupported_titles if unsupportedTitles[pagename] then return "Unsupported titles/" .. unsupportedTitles[pagename] end end -- Set `unsupported` as true if certain conditions are met. local unsupported -- Check if there's an unsupported character. \239\191\189 is the replacement character U+FFFD, which can't be typed -- directly here due to an abuse filter. Unix-style dot-slash notation is also unsupported, as it is used for -- relative paths in links, as are 3 or more consecutive tildes. Note: match is faster with magic -- characters/charsets; find is faster with plaintext. if ( match(pagename, "[#<>%[%]_{|}]") or find(pagename, "\239\191\189") or match(pagename, "%f[^%z/]%.%.?%f[%z/]") or find(pagename, "~~~") ) then unsupported = true -- If it looks like an interwiki link. elseif find(pagename, ":") then local prefix = gsub(pagename, "^:*(.-):.*", ulower) if ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then unsupported = true end end -- Escape unsupported characters so they can be used in titles. ` is used as a delimiter for this, so a raw use of -- it in an unsupported title is also escaped here to prevent interference; this is only done with unsupported -- titles, though, so inclusion won't in itself mean a title is treated as unsupported (which is why it's excluded -- from the earlier test). if unsupported then -- FIXME: This conversion needs to be different for reconstructed pages with unsupported characters. There -- aren't any currently, but if there ever are, we need to fix this e.g. to put them in something like -- Reconstruction:Proto-Indo-European/Unsupported titles/`lowbar``num`. local unsupported_characters = load_data(links_data_module).unsupported_characters pagename = pagename:gsub("[#<>%[%]_`{|}\239]\191?\189?", unsupported_characters) :gsub("%f[^%z/]%.%.?%f[%z/]", function(m) return (gsub(m, "%.", "`period`")) end) :gsub("~~~+", function(m) return (gsub(m, "~", "`tilde`")) end) pagename = "Unsupported titles/" .. pagename elseif not is_reconstructed_or_appendix then -- Check if this is a mammoth page. If so, which subpage should we link to? local m_links_data = load_data(links_data_module) local mammoth_page_type = m_links_data.mammoth_pages[pagename] if mammoth_page_type then local canonical_name = self:getFullName() if canonical_name ~= "Translingual" and canonical_name ~= "English" then local this_subpage local L2_sort_key = get_L2_sort_key(canonical_name) for _, subpage_spec in ipairs(m_links_data.mammoth_page_subpage_types[mammoth_page_type]) do -- unpack() fails utterly on data loaded using mw.loadData() even if offsets are given local subpage, pattern = subpage_spec[1], subpage_spec[2] if pattern == true or L2_sort_key:match(pattern) then this_subpage = subpage break end end if not this_subpage then error(("Internal error: Bad data in mammoth_page_subpage_pages in [[Module:links/data]] for mammoth page %s, type %s; last entry didn't have 'true' in it"):format( pagename, mammoth_page_type)) end pagename = pagename .. "/" .. this_subpage end end end return (initial_asterisk or "") .. pagename end --[==[ Strip the diacritics from a display pagename and convert the resulting logical pagename into a physical pagename. This allows you, for example, to retrieve the contents of the page or check its existence. WARNING: This is deprecated and will be going away. It is a simple composition of `##Language:stripDiacritics` and `##Language:logicalToPhysical`; most callers only want the former, and if you need both, call them both yourself. `text` and `sc` are as in `##Language:stripDiacritics`, and `is_reconstructed_or_appendix` is as in `##Language:logicalToPhysical`. ]==] function Language:makeEntryName(text, sc, is_reconstructed_or_appendix) track("makeEntryName called") return self:logicalToPhysical(self:stripDiacritics(text, sc), is_reconstructed_or_appendix) end --[==[ Generate term alternants (e.g. simplified versions of traditional Chinese terms) using a language-specific method, and return them as a table. If the language provides no method for generating alternants, return a table containing only the input term. ]==] function Language:generateAlternants(text, sc) local generate_alternants = self._data.generate_alternants if generate_alternants == nil then return {text} end sc = checkScript(text, self, sc) return require("Module:" .. generate_alternants).generateAlternants(text, self, sc) end --[==[ Create a sort key for the given stripped text, following the rules appropriate for the language. This removes diacritical marks from the stripped text if they are not considered significant for sorting, and may perform some other changes. Any initial hyphen is also removed, and anything in parentheses is removed as well. The `sort_key` setting for each language in the data modules defines the replacements made by this function, or it gives the name of the module that takes the stripped text and returns a sortkey. ]==] function Language:makeSortKey(text, sc) if (not text) or text == "" then return text end if match(text, "<[^<>]+>") then track("track HTML tag") end -- Remove directionality/control characters, bold, italics, soft hyphens, strip markers and HTML tags. -- FIXME: Partly duplicated with remove_formatting() in [[Module:links]]. text = ugsub(text, "[\194\173\216\156\226\128\142\226\128\143\226\128\170-\226\128\174\226\129\166-\226\129\175]", "") text = text:gsub("('*)'''(.-'*)'''", "%1%2"):gsub("('*)''(.-'*)''", "%1%2") text = gsub(unstrip(text), "<[^<>]+>", "") text = decode_uri(text, "PATH") text = checkNoEntities(text) -- Remove initial hyphens and * unless the term only consists of spacing + punctuation characters. text = ugsub(text, "^([􀀀-􏿽]*)[-־ـ᠊*]+([􀀀-􏿽]*)(.*[^%s%p].*)", "%1%2%3") sc = checkScript(text, self, sc) text = normalize(text, sc) text = removeCarets(text, sc) -- For languages with dotted dotless i, ensure that "İ" is sorted as "i", and "I" is sorted as "ı". if self:hasDottedDotlessI() then text = gsub(text, "I\204\135", "i") -- decomposed "İ" :gsub("I", "ı") text = sc:toFixedNFD(text) end -- Convert to lowercase, make the sortkey, then convert to uppercase. Where the language has dotted dotless i, it is -- usually not necessary to convert "i" to "İ" and "ı" to "I" first, because "I" will always be interpreted as -- conventional "I" (not dotless "İ") by any sorting algorithms, which will have been taken into account by the -- sortkey substitutions themselves. However, if no sortkey substitutions have been specified, then conversion is -- necessary so as to prevent "i" and "ı" both being sorted as "I". -- -- An exception is made for scripts that (sometimes) sort by scraping page content, as that means they are sensitive -- to changes in capitalization (as it changes the target page). if not sc:sortByScraping() then text = ulower(text) end local actual_substitution_data, _ -- Don't trim whitespace here because it's significant at the beginning of a sort key or sort base. text, _, actual_substitution_data = iterateSectionSubstitutions(self, text, sc, nil, nil, self._data.sort_key, "sort_key", "makeSortKey", "notrim") if not sc:sortByScraping() then if self:hasDottedDotlessI() and not actual_substitution_data then text = text:gsub("ı", "I"):gsub("i", "İ") text = sc:toFixedNFC(text) end text = uupper(text) end -- Remove parentheses, as long as they are either preceded or followed by something. text = gsub(text, "(.)[()]+", "%1"):gsub("[()]+(.)", "%1") text = escape_risky_characters(text) return text end --[==[ Create the form used as as a basis for display text and transliteration. FIXME: Rename to correctInputText(). ]==] local function processDisplayText(text, self, sc, keepCarets, keepPrefixes) local subbedChars = {} text, subbedChars = doTempSubstitutions(text, subbedChars, keepCarets) text = decode_uri(text, "PATH") text = checkNoEntities(text) sc = checkScript(text, self, sc) text = normalize(text, sc) text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, self._data.display_text, "display_text", "makeDisplayText") text = removeCarets(text, sc) -- Remove any interwiki link prefixes (unless they have been escaped or this has been disabled). if find(text, ":") and not keepPrefixes then local rep repeat text, rep = gsub(text, "\\\\(\\*:)", "\3%1") until rep == 0 text = gsub(text, "\\:", "\4") while true do local prefix = gsub(text, "^(.-):.+", function(m1) return (gsub(m1, "\244[\128-\191]*", "")) end) -- Check if the prefix is an interwiki, though ignore capitalised Wiktionary:, which is a namespace. if not prefix or prefix == text or prefix == "Wiktionary" or not (load_data("Module:data/interwikis")[ulower(prefix)] or prefix == "") then break end text = gsub(text, "^(.-):(.*)", function(m1, m2) local ret = {} for subbedChar in gmatch(m1, "\244[\128-\191]*") do insert(ret, subbedChar) end return concat(ret) .. m2 end) end text = gsub(text, "\3", "\\"):gsub("\4", ":") end return text, subbedChars end --[==[ Make the display text (i.e. what is displayed on the page). ]==] function Language:makeDisplayText(text, sc, keepPrefixes) if not text or text == "" then return text end local subbedChars text, subbedChars = processDisplayText(text, self, sc, nil, keepPrefixes) text = escape_risky_characters(text) return undoTempSubstitutions(text, subbedChars) end --[==[ Transliterate the text from the given script into Latin script (see [[Wiktionary:Transliteration and romanization]]). The language must have the `translit` property for this to work; if it is not present, {nil} is returned. The `sc` parameter is handled by the transliteration module, and how it is handled is specific to that module. Some transliteration modules may tolerate {nil} as the script, others require it to be one of the possible scripts that the module can transliterate, and will throw an error if it's not one of them. For this reason, the `sc` parameter should always be provided when writing non-language-specific code. The `module_override` parameter is used to override the default module that is used to provide the transliteration. This is useful in cases where you need to demonstrate a particular module in use, but there is no default module yet, or you want to demonstrate an alternative version of a transliteration module before making it official. It should not be used in real modules or templates, only for testing. All uses of this parameter are tracked by [[Wiktionary:Tracking/languages/module_override]]. '''Known bugs''': * This function assumes {tr(s1) .. tr(s2) == tr(s1 .. s2)}. When this assertion fails, wikitext markups like <nowiki>'''</nowiki> can cause wrong transliterations. * HTML entities like `&amp;apos;`, often used to escape wikitext markups, do not work. ]==] function Language:transliterate(text, sc, module_override) -- If there is no text, or the language doesn't have transliteration data and there's no override, return nil. if not text or text == "" or text == "-" then return text end -- If the script is not transliteratable (and no override is given), return nil. sc = checkScript(text, self, sc) if not (sc:isTransliterated() or module_override) then -- temporary tracking to see if/when this gets triggered track("non-transliterable") track("non-transliterable/" .. self._code) track("non-transliterable/" .. sc:getCode()) track("non-transliterable/" .. sc:getCode() .. "/" .. self._code) return nil end -- Remove any strip markers. text = unstrip(text) -- Do not process the formatting into PUA characters for certain languages. local processed = load_data(languages_data_module).substitution[self._code] ~= "none" -- Get the display text with the keepCarets flag set. local subbedChars if processed then text, subbedChars = processDisplayText(text, self, sc, true) end -- Transliterate (using the module override if applicable). text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, true, module_override or self._data.translit, "translit", "tr") if not text then return nil end -- Incomplete transliterations return nil. local charset = sc.characters if charset and umatch(text, "[" .. charset .. "]") then -- Remove any characters in Latin, which includes Latin characters also included in other scripts (as these are -- false positives), as well as any PUA substitutions. Anything remaining should only be script code "None" -- (e.g. numerals). local check_text = ugsub(text, "[" .. get_script("Latn").characters .. "􀀀-􏿽]+", "") -- Set none_is_last_resort_only flag, so that any non-None chars will cause a script other than "None" to be -- returned. if find_best_script_without_lang(check_text, true):getCode() ~= "None" then return nil end end if processed then text = escape_risky_characters(text) text = undoTempSubstitutions(text, subbedChars) end -- If the script does not use capitalization, then capitalize any letters of the transliteration which are -- immediately preceded by a caret (and remove the caret). if text and not sc:hasCapitalization() and text:find("^", 1, true) then text = processCarets(text, "%^([\128-\191\244]*%*?)([^\128-\191\244][\128-\191]*)", function(m1, m2) return m1 .. uupper(m2) end) end -- Track module overrides. if module_override ~= nil then track("module_override") end return text end do local function handle_language_spec(self, spec, sc) local ret = self["_" .. spec] if ret == nil then ret = self._data[spec] if type(ret) == "string" then ret = list_to_set(split(ret, ",", true, true)) end self["_" .. spec] = ret end if type(ret) == "table" then ret = ret[sc:getCode()] end return not not ret end function Language:overrideManualTranslit(sc) return handle_language_spec(self, "override_translit", sc) end function Language:link_tr(sc) return handle_language_spec(self, "link_tr", sc) end end --[==[ Return {true} if the language has a transliteration module, or {false} if it doesn't. ]==] function Language:hasTranslit() return not not self._data.translit end --[==[ Return {true} if the language uses the letters I/ı and İ/i, or {false} if it doesn't.]==] function Language:hasDottedDotlessI() return not not self._data.dotted_dotless_i end function Language:toJSON(opts) local strip_diacritics, strip_diacritics_patterns, strip_diacritics_remove_diacritics = self._data.strip_diacritics if strip_diacritics then if strip_diacritics.from then strip_diacritics_patterns = {} for i, from in ipairs(strip_diacritics.from) do insert(strip_diacritics_patterns, {from = from, to = strip_diacritics.to[i] or ""}) end end strip_diacritics_remove_diacritics = strip_diacritics.remove_diacritics end -- mainCode should only end up non-nil if dontCanonicalizeAliases is passed to make_object(). -- props should either contain zero-argument functions to compute the value, or the value itself. local props = { ancestors = function() return self:getAncestorCodes() end, canonicalName = function() return self:getCanonicalName() end, categoryName = function() return self:getCategoryName("nocap") end, code = self._code, mainCode = self._mainCode, parent = function() return self:getParentCode() end, full = function() return self:getFullCode() end, stripDiacriticsPatterns = strip_diacritics_patterns, stripDiacriticsRemoveDiacritics = strip_diacritics_remove_diacritics, family = function() return self:getFamilyCode() end, aliases = function() return self:getAliases() end, varieties = function() return self:getVarieties() end, otherNames = function() return self:getOtherNames() end, pseudoFamilies = function() return self:getPseudoFamilies() end, scripts = function() return self:getScriptCodes() end, type = function() return keys_to_list(self:getTypes()) end, wikimediaLanguages = function() return self:getWikimediaLanguageCodes() end, wikidataItem = function() return self:getWikidataItem() end, wikipediaArticle = function() return self:getWikipediaArticle(true) end, } local ret = {} for prop, val in pairs(props) do if not opts.skip_fields or not opts.skip_fields[prop] then if type(val) == "function" then ret[prop] = val() else ret[prop] = val end end end -- Use `deep_copy` when returning a table, so that there are no editing restrictions imposed by `mw.loadData`. return opts and opts.lua_table and deep_copy(ret) or to_json(ret, opts) end function export.getDataModuleName(code) local letter = match(code, "^(%l)%l%l?$") return "Module:" .. ( letter == nil and "languages/data/exceptional" or #code == 2 and "languages/data/2" or "languages/data/3/" .. letter ) end get_data_module_name = export.getDataModuleName function export.getExtraDataModuleName(code) return get_data_module_name(code) .. "/extra" end get_extra_data_module_name = export.getExtraDataModuleName do local function make_stack(data) local key_types = { [2] = "unique", aliases = "unique", other_names = "unique", type = "append", varieties = "unique", wikipedia_article = "unique", wikimedia_codes = "unique" } local function __index(self, k) local stack, key_type = getmetatable(self), key_types[k] -- Data that isn't inherited from the parent. if key_type == "unique" then local v = stack[stack[make_stack]][k] if v == nil then local layer = stack[0] if layer then -- Could be false if there's no extra data. v = layer[k] end end return v -- Data that is appended by each generation. elseif key_type == "append" then local parts, offset, n = {}, 0, stack[make_stack] for i = 1, n do local part = stack[i][k] if part == nil then offset = offset + 1 else parts[i - offset] = part end end return offset ~= n and concat(parts, ",") or nil end local n = stack[make_stack] while true do local layer = stack[n] if not layer then -- Could be false if there's no extra data. return nil end local v = layer[k] if v ~= nil then return v end n = n - 1 end end local function __newindex() error("table is read-only") end local function __pairs(self) -- Iterate down the stack, caching keys to avoid duplicate returns. local stack, seen = getmetatable(self), {} local n = stack[make_stack] local iter, state, k, v = pairs(stack[n]) return function() repeat repeat k = iter(state, k) if k == nil then n = n - 1 local layer = stack[n] if not layer then -- Could be false if there's no extra data. return nil end iter, state, k = pairs(layer) end until not (k == nil or seen[k]) -- Get the value via a lookup, as the one returned by the -- iterator will be the raw value from the current layer, -- which may not be the one __index will return for that -- key. Also memoize the key in `seen` (even if the lookup -- returns nil) so that it doesn't get looked up again. -- TODO: store values in `self`, avoiding the need to create -- the `seen` table. The iterator will need to iterate over -- `self` with `next` first to find these on future loops. v, seen[k] = self[k], true until v ~= nil return k, v end end local __ipairs = require(table_module).indexIpairs function make_stack(data) local stack = { data, [make_stack] = 1, -- stores the length and acts as a sentinel to confirm a given metatable is a stack. __index = __index, __newindex = __newindex, __pairs = __pairs, __ipairs = __ipairs, } stack.__metatable = stack return setmetatable({}, stack), stack end return make_stack(data) end local function get_stack(data) local stack = getmetatable(data) return stack and type(stack) == "table" and stack[make_stack] and stack or nil end --[==[ <span style="color: var(--wikt-palette-red,#BA0000)">This function is not for use in entries or other content pages.</span> Return a blob of data about the language. The format of this blob is undocumented, and perhaps unstable; it's intended for things like the module's own unit-tests, which are "close friends" with the module and will be kept up-to-date as the format changes. If `extra` is set, any extra data in the relevant `/extra` module will be included. (Note that it will be included anyway if it has already been loaded into the language object.) If `raw` is set, then the returned data will not contain any data inherited from parent objects. -- Do NOT use these methods! -- All uses should be pre-approved on the talk page! ]==] function Language:getData(extra, raw) if extra then self:loadInExtraData() end local data = self._data -- If raw is not set, just return the data. if not raw then return data end local stack = get_stack(data) -- If there isn't a stack or its length is 1, return the data. Extra data (if any) will be included, as it's -- stored at key 0 and doesn't affect the reported length. if stack == nil then return data end local n = stack[make_stack] if n == 1 then return data end extra = stack[0] -- If there isn't any extra data, return the top layer of the stack. if extra == nil then return stack[n] end -- If there is, return a new stack which has the top layer at key 1 and the extra data at key 0. data, stack = make_stack(stack[n]) stack[0] = extra return data end function Language:loadInExtraData() -- Only full languages have extra data. if not self:hasType("language", "full") then return end local data = self._data -- If there's no stack, create one. local stack = get_stack(self._data) if stack == nil then data, stack = make_stack(data) -- If already loaded, return. elseif stack[0] ~= nil then return end self._data = data -- Load extra data from the relevant module and add it to the stack at key 0, so that the __index and __pairs -- metamethods will pick it up, since they iterate down the stack until they run out of layers. local code = self._code local modulename = get_extra_data_module_name(code) -- No data cached as false. stack[0] = modulename and load_data(modulename)[code] or false end --[==[ Return the name of the module containing the language's data, e.g. [[Module:languages/data/3/k]] for three-letter codes beginning with `k`. ]==] function Language:getDataModuleName() local name = self._dataModuleName if name == nil then name = self:hasType("etymology-only") and etymology_languages_data_module or get_data_module_name(self._mainCode or self._code) self._dataModuleName = name end return name end --[==[ Return the name of the module containing the language's extra data, e.g. [[Module:languages/data/3/k/extra]] for three-letter codes beginning with `k`. ]==] function Language:getExtraDataModuleName() local name = self._extraDataModuleName if name == nil then name = not self:hasType("etymology-only") and get_extra_data_module_name(self._mainCode or self._code) or false self._extraDataModuleName = name end return name or nil end function export.makeObject(code, data, dontCanonicalizeAliases) local data_type = type(data) if data_type ~= "table" then error(("bad argument #2 to 'makeObject' (table expected, got %s)"):format(data_type)) end -- Convert any aliases. local input_code = code code = normalize_code(code) input_code = dontCanonicalizeAliases and input_code or code local parent if data.parent then parent = get_by_code(data.parent, nil, true, true) else parent = Language end parent.__index = parent local lang = {_code = input_code} -- This can only happen if dontCanonicalizeAliases is passed to make_object(). if code ~= input_code then lang._mainCode = code end local parent_data = parent._data if parent_data == nil then -- Full code is the same as the code. lang._fullCode = parent._code or code else -- Copy full code. lang._fullCode = parent._fullCode local stack = get_stack(parent_data) if stack == nil then parent_data, stack = make_stack(parent_data) end -- Insert the input data as the new top layer of the stack. local n = stack[make_stack] + 1 data, stack[n], stack[make_stack] = parent_data, data, n end lang._data = data return setmetatable(lang, parent) end make_object = export.makeObject end --[==[ Find the language whose code matches the one provided. If it exists, return a `Language` object representing the language. Otherwise, return {nil}, unless `paramForError` is given, in which case an error is generated. If `paramForError` is {true}, a generic error message mentioning the bad code is generated; otherwise `paramForError` should be a string or number specifying the parameter that the code came from, and this parameter will be mentioned in the error message along with the bad code. If `allowEtymLang` is specified, etymology-only language codes are allowed and looked up along with full language codes. If `allowFamily` is specified, language family codes are allowed and looked up along with normal language codes. ]==] function export.getByCode(code, paramForError, allowEtymLang, allowFamily) -- [[ Track uses of paramForError, ultimately so it can be removed, as error-handling should be done by -- [[Module:parameters]], not here. ]] -- I (Benwing) disagree; that is an idealistic goal unlikely to be achievable -- realistically. if paramForError ~= nil then track("paramForError") end if type(code) ~= "string" then local typ if not code then typ = "nil" elseif check_object("language", true, code) then typ = "a language object" elseif check_object("family", true, code) then typ = "a family object" else typ = "a " .. type(code) end error("The function getByCode expects a string as its first argument, but received " .. typ .. ".") end local m_data = load_data(languages_data_module) if m_data.aliases[code] or m_data.track[code] then track(code) end local norm_code = normalize_code(code) -- Get the data, checking for etymology-only languages if allowEtymLang is set. local data = load_data(get_data_module_name(norm_code))[norm_code] or allowEtymLang and load_data(etymology_languages_data_module)[norm_code] -- If no data was found and allowFamily is set, check the family data. If the main family data was found, make the -- object with [[Module:families]] instead, as family objects have different methods. However, if it's an -- etymology-only family, use make_object in this module (which handles object inheritance), and the -- family-specific methods will be inherited from the parent object. if data == nil and allowFamily then data = load_data("Module:families/data")[norm_code] if data ~= nil then if data.parent == nil then return make_family_object(norm_code, data) elseif not allowEtymLang then data = nil end end end local retval = code and data and make_object(code, data) if not retval and paramForError then require("Module:languages/errorGetBy").code(code, paramForError, allowEtymLang, allowFamily) end return retval end get_by_code = export.getByCode --[==[ Find the language whose canonical name (the name used to represent that language on Wiktionary) or other name matches the one provided. If it exists, return a `Language` object representing the language. Otherwise, return {nil}, unless `paramForError` is given, in which case an error is generated. If `allowEtymLang` is specified, etymology-only language codes are allowed and looked up along with full language codes. If `allowFamily` is specified, language family codes are allowed and looked up along with normal language codes. The canonical name of languages should always be unique (it is an error for two languages on Wiktionary to share the same canonical name), so this is guaranteed to give at most one result. This function is powered by [[Module:languages/canonical names]], which contains a pre-generated mapping of full-language canonical names to codes. It is generated by going through the [[:Category:Language data modules]] for full languages. When `allowEtymLang` is specified for the above function, [[Module:etymology languages/canonical names]] may also be used, and when `allowFamily` is specified for the above function, [[Module:families/canonical names]] may also be used. ]==] function export.getByCanonicalName(name, errorIfInvalid, allowEtymLang, allowFamily) local byName = load_data("Module:languages/canonical names") local code = byName and byName[name] if not code and allowEtymLang then byName = load_data("Module:etymology languages/canonical names") code = byName and byName[name] or byName[gsub(name, " [Ss]ubstrate$", "")] or byName[gsub(name, "^a ", "")] or byName[gsub(name, "^a ", ""):gsub(" [Ss]ubstrate$", "")] or -- For etymology families like "ira-pro". -- FIXME: This is not ideal, as it allows " languages" to be appended to any etymology-only language, too. byName[match(name, "^(.*) languages$")] end if not code and allowFamily then byName = load_data("Module:families/canonical names") code = byName[name] or byName[match(name, "^(.*) languages$")] end local retval = code and get_by_code(code, errorIfInvalid, allowEtymLang, allowFamily) if not retval and errorIfInvalid then require("Module:languages/errorGetBy").canonicalName(name, allowEtymLang, allowFamily) end return retval end --[==[ Used by [[Module:languages/data/2]] (et al.) and [[Module:etymology languages/data]], [[Module:families/data]], [[Module:scripts/data]] and [[Module:writing systems/data]] to finalize the data into the format that is actually returned. ]==] function export.finalizeData(data, main_type, variety) local fields = {"type"} if main_type == "language" then insert(fields, 4) -- script codes insert(fields, "ancestors") insert(fields, "link_tr") insert(fields, "override_translit") insert(fields, "wikimedia_codes") elseif main_type == "script" then insert(fields, 3) -- writing system codes end -- Families and writing systems have no extra fields to process. local fields_len = #fields for _, entity in next, data do if variety then -- Move parent from 3 to "parent" and family from "family" to 3. These are different for the sake of -- convenience, since very few varieties have the family specified, whereas all of them have a parent. entity.parent, entity[3] = entity[3], entity.family entity.family = nil -- Give the type "regular" iff not a variety and no other types are assigned. elseif not (entity.type or entity.parent) then entity.type = "regular" end for i = 1, fields_len do local key = fields[i] local field = entity[key] if field and type(field) == "string" then entity[key] = gsub(field, "%s*,%s*", ",") end end end return data end --[==[ For backwards compatibility only; modules should require the error themselves. ]==] function export.err(lang_code, param, code_desc, template_tag, not_real_lang) return require("Module:languages/error")(lang_code, param, code_desc, template_tag, not_real_lang) end return export mz43zcz6drvto1xyal5io97jdfkt6jx Module:languages/data 828 7869 54703 54387 2026-09-25T17:38:56Z Deadend0914 7211 54703 Scribunto text/plain local export = {} -- We can't use mw.loadData() on [[Module:languages/chars]] because [[Module:languages/data]] itself is sometimes loaded -- using mw.loadData(), and calling mw.loadData() on [[Module:languages/chars]] will insert metatables into the -- character tables, which the second mw.loadData() will choke on. local m_chars = require("Module:languages/chars") local u = require("Module:string/char") local c = m_chars.chars export.chars = c local p = m_chars.puaChars export.puaChars = p local cs = m_chars.chars_substitutions export.chars_substitutions = cs -- FIXME! Many of the script-specific values below can be moved to [[Module:scripts/data]] to serve as script-wide -- fallback values instead of specifying them for each language using the script. local s = {} -- These values are placed here to make it possible to synchronise a group of languages without the need for a dedicated function module. -- cau do local cau_remove_diacritics = c.grave .. c.acute .. c.macron local cau_from = {"[IlΙІӀᴴ]"} local cau_to = {{ ["l"] = "ӏ", ["Ι"] = "ӏ", ["І"] = "ӏ", ["Ӏ"] = "ӏ", ["ᴴ"] = "ᵸ", }} s["cau-Cyrl-displaytext"] = { from = cau_from, to = cau_to, } s["cau-Cyrl-stripdiacritics"] = { remove_diacritics = cau_remove_diacritics, from = cau_from, to = cau_to, } s["cau-Latn-stripdiacritics"] = {remove_diacritics = cau_remove_diacritics} end s["itc-Latn-displaytext"] = { from = {c.caron}, to = {c.breve}, } s["itc-Latn-stripdiacritics"] = {remove_diacritics = c.macron .. c.breve .. c.diaer .. c.caron .. c.dinvbreve} s["itc-Latn-sortkey"] = { remove_diacritics = c.circ .. c.tilde .. c.macron .. c.breve .. c.diaer .. c.caron .. c.zigzag .. c.dmacron .. c.dtilde .. c.dinvbreve .. c.small_a .. c.small_e .. c.small_i .. c.small_o .. c.small_u, -- Chiefly medieval abbreviations. from = {"ᵃ", "æ", "[đꝱꟈ]", "ᵉ", "ⁱ", "ꝁ", "[ƚꝉꝲ]", "ꝳ", "ꝴ", "[ꝋᵒ]", "œ", "[ꝑꝓꝕ]", "[ꝗꝙ]", "[ꝛꝵꝶꝝ]", "[ꟊˢ]", "[ꝷᵗ]", "ᵘ", "ꝟ", "⁊"}, to = {"a", "ae", "d", "e", "i", "k", "l", "m", "n", "o", "oe", "p", "q", "r", "s", "t", "u", "v", "&"} } s["Jpan-standardchars"] = -- exclude ぢづヂヅ "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちっつてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろん" .. "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチッツテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロン" local jpx_displaytext = { from = {"~", "="}, to = {"〜", "゠"} } s["jpx-displaytext"] = { Jpan = jpx_displaytext, Hani = jpx_displaytext, Hrkt = jpx_displaytext, Hira = jpx_displaytext, Kana = jpx_displaytext -- not Latn or Brai } s["jpx-stripdiacritics"] = s["jpx-displaytext"] s["jpx-sortkey"] = { Jpan = "Jpan-sortkey", Hani = "Hani-sortkey", Hrkt = "Hira-sortkey", -- sort general kana by normalizing to Hira Hira = "Hira-sortkey", Kana = "Kana-sortkey", Latn = {remove_diacritics = c.tilde .. c.macron .. c.diaer} } s["jpx-translit"] = { Hrkt = "Hrkt-translit", Hira = "Hrkt-translit", Kana = "Hrkt-translit" } s["roa-oil-sortkey"] = { remove_diacritics = c.grave .. c.acute .. c.circ .. c.tilde .. c.diaer .. c.ringabove .. c.cedilla .. "'", from = {"æ", "œ", "·"}, to = {"ae", "oe", " "} } s["wen-sortkey"] = { from = {"ch", "[lłßꞩẜ]", "dz[" .. c.caron .. c.acute .. "]", "[bcefmnoprswz][" .. c.caron .. c.acute .. c.dotabove .. "]"}, to = { "h" .. p[1], { ["l"] = "l" .. p[1], ["ł"] = "l", ["ß"] = "s", ["ꞩ"] = "š", ["ẜ"] = "š", }, { ["dz" .. c.caron] = "d" .. p[1], ["dz" .. c.acute] = "d" .. p[2] }, { ["b" .. c.acute] = "b" .. p[1], ["c" .. c.caron] = "c" .. p[1], ["c" .. c.acute] = "c" .. p[2], ["e" .. c.caron] = "e" .. p[1], ["e" .. c.dotabove] = "e" .. p[1], ["f" .. c.acute] = "f" .. p[1], ["m" .. c.acute] = "m" .. p[1], ["n" .. c.acute] = "n" .. p[1], ["o" .. c.acute] = "o" .. p[1], ["p" .. c.acute] = "p" .. p[1], ["r" .. c.caron] = "r" .. p[1], ["r" .. c.acute] = "r" .. p[2], ["s" .. c.caron] = "s" .. p[1], ["s" .. c.acute] = "s" .. p[2], ["w" .. c.acute] = "w" .. p[1], ["z" .. c.caron] = "z" .. p[1], ["z" .. c.acute] = "z" .. p[2], } } } -- Myanmar dotted form : https://www.unicode.org/Public/UNIDATA/StandardizedVariants.txt s["aio-displaytext"] = { from = {"([ကဂငတထပမယလဝဢေၵၸၺႀꩠꩡꩢꩣꩤꩥꩦꩫꩬꩯꩺ])"}, to = {"%1" .. c.VS01} } s["aio-stripdiacritics"] = { remove_diacritics = c.VS01, } s["phk-displaytext"] = s["aio-displaytext"] s["phk-stripdiacritics"] = s["aio-stripdiacritics"] s["kht-displaytext"] = s["aio-displaytext"] s["kht-stripdiacritics"] = s["aio-stripdiacritics"] export.shared = s --[==[ var: Short-term solution to override the standard substitution process, by forcing the module to substitute the entire text in one pass, if "cont" is given. This results in any PUA characters that are used as stand-ins for formatting being handled by the language-specific substitution process, which is usually undesirable. If the value is "none" then the formatting tags do not get turned into PUA characters in the first place. This override is provided for languages which use formatting between strings of text which might need to interact with each other (e.g. Korean 값이 transliterates as "gaps-i", but [[값]] has the formatting '''값'''[[-이]]. The normal process would split the text at the second '''.) ]==] export.substitution = { ["gmy"] = "none", ["ja"] = "cont", ["jje"] = "cont", ["ket"] = "none", ["ko"] = "cont", ["ko-ear"] = "cont", ["ru"] = "cont", ["th-new"] = "cont", ["sa"] = "cont", ["zkt"] = "cont", } --[==[ var: Code aliases. The left side is the alias and the right side is the canonical code. NOTE: These are gradually being deprecated, so should not be added to on a permanent basis. Temporary additions are permitted under reasonable circumstances (e.g. to facilitate changing a language's code). When an alias is no longer used, it should be removed. Aliases in this table are tracked at [[Wiktionary:Tracking/languages/LANG]]; see e.g. [[Special:WhatLinksHere/Wiktionary:Tracking/languages/VL.]] for the `VL.` alias. ]==] export.aliases = { ["EL."] = "la-ecc", ["LL."] = "la-lat", ["ML."] = "la-med", ["NL."] = "la-new", ["VL."] = "la-vul", ["grc-epc"] = "grc-epi", ["nds-DE"] = "nds-de", ["nds-NL"] = "nds-nl", ["roa-oan"] = "roa-ona", ["sa-cls"] = "cls", ["sa-ved"] = "vsn", } --[==[ var: Codes which are tracked. Note that all aliases listed above are also tracked, so should not be duplicated here. Tracking uses the same mechanism described above in the comment above `export.aliases`. ]==] export.track = { -- Codes duplicated between full and etymology-only languages. ["lzh-lit"] = true, ["lzh"] = true, -- Languages actively being converted to families. ["bh"] = true, -- inc-bih ["nan"] = true, -- zhx-nan -- Transcription scheme being changed. ["trk-pro"] = true, } return export sybykcugwy6ckm7y09iyst74xywteqw Module:scripts 828 7871 54735 54389 2026-09-25T21:18:25Z Deadend0914 7211 54735 Scribunto text/plain local export = {} local combining_classes_module = "Module:Unicode data/combining classes" local debug_track_module = "Module:debug/track" local json_module = "Module:JSON" local language_like_module = "Module:language-like" local load_module = "Module:load" local scripts_canonical_names_module = "Module:scripts/canonical names" local scripts_chartoscript_module = "Module:scripts/charToScript" local scripts_data_module = "Module:scripts/data" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local writing_systems_module = "Module:writing systems" local writing_systems_data_module = "Module:writing systems/data" local concat = table.concat local get_by_code -- Defined below. local gmatch = string.gmatch local insert = table.insert local make_object -- Defined below. local match = string.match local require = require local select = select local setmetatable = setmetatable local toNFC = mw.ustring.toNFC local toNFD = mw.ustring.toNFD local toNFKC = mw.ustring.toNFKC local toNFKD = mw.ustring.toNFKD local type = type --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function category_name_has_suffix(...) category_name_has_suffix = require(language_like_module).categoryNameHasSuffix return category_name_has_suffix(...) end local function category_name_to_code(...) category_name_to_code = require(language_like_module).categoryNameToCode return category_name_to_code(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function explode(...) explode = require(string_utilities_module).explode_utf8 return explode(...) end local function get_writing_system(...) get_writing_system = require(writing_systems_module).getByCode return get_writing_system(...) end local function keys_to_list(...) keys_to_list = require(table_module).keysToList return keys_to_list(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function to_json(...) to_json = require(json_module).toJSON return to_json(...) end local function track(page) debug_track("scripts/" .. page) return true end local function ugsub(...) ugsub = require(string_utilities_module).gsub return ugsub(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local scripts_canonical_names local function get_scripts_canonical_names() scripts_canonical_names, get_scripts_canonical_names = load_data(scripts_canonical_names_module), nil return scripts_canonical_names end local scripts_data local function get_scripts_data() scripts_data, get_scripts_data = load_data(scripts_data_module), nil return scripts_data end local scripts_suffixes local function get_scripts_suffixes() scripts_suffixes, get_scripts_suffixes = { "script", "code", "notation", "letters", "numerals", "semaphore", }, nil for _, v in pairs(load_data(writing_systems_data_module)) do insert(scripts_suffixes, v[1]) end return scripts_suffixes end local Script = {} Script.__index = Script --[==[Returns the script code of the script. Example: {{lua|"Cyrl"}} for Cyrillic.]==] function Script:getCode() return self._code end --[==[ Return the canonical name of the script. This is the name used to represent that script on Wiktionary. Example: {"Cyrillic"} for Cyrillic. If `lang` is specified and the script has a language-specific name, return that (e.g. {"Shahmukhi"} for script `Aran` with Punjabi and certain related languages); otherwise, return the default name (if the script has different names in different languages, e.g. {"Arabic"} for `Aran`), or the only name if there is only one. ]==] function Script:getCanonicalName(lang) local rawdata = self._data[1] if type(rawdata) == "string" then return rawdata end local name if lang then name = rawdata[lang:getCode()] if name then return name end name = rawdata[lang:getFullCode()] if name then return name end end name = rawdata.default if not name then error(("Internal error: no default key in script name table for script %s"):format(self:getCode())) end return name end --[==[ Return the table mapping languages to names for the script. If the script has only one name (as most scripts do), return a string consisting of that name. Otherwise, return a table mapping language codes to names, where the key named `default` contains the default name used for languages not specified in the table. ]==] function Script:getCanonicalNameTable() return self._data[1] end --[==[ Return all canonical names of the script. The default name is first. ]==] function Script:getCanonicalNames() local rawdata = self._data[1] if type(rawdata) == "string" then return {rawdata} end local names = {} for lang, name in pairs(rawdata) do if lang ~= "default" then require(table_module).insertIfNot(names, name) end end table.sort(names) require(table_module).insertIfNot(names, rawdata.default, {pos = 1}) return names end --[==[ Return the display form of the script. For scripts, this is the same as the value returned by {:getCategoryName("nocap")}, i.e. it reads <code>"<var>NAME</var> script"</code> (e.g. {"Arabic script"}). The displayed text used in {:makeCategoryLink()} is always the same as the display form. If the script has different names in different languages (e.g. `Aran`, which is called {"Shahmukhi"} in Punjabi and certain related languages but otherwise {"Arabic"}), and `lang` is given, return the language-specific name; otherwise return the default or only name. ]==] function Script:getDisplayForm(lang) return self:getCategoryName("nocap", lang) end function Script:getAliases() Script.getAliases = require(language_like_module).getAliases return self:getAliases() end function Script:getVarieties(flatten) Script.getVarieties = require(language_like_module).getVarieties return self:getVarieties(flatten) end function Script:getOtherNames() Script.getOtherNames = require(language_like_module).getOtherNames return self:getOtherNames() end function Script:getAllNames() Script.getAllNames = require(language_like_module).getAllNames return self:getAllNames() end --[==[Returns the {{w|IETF language tag#Syntax of language tags|IETF subtag}} used for the script, which should always be a valid {{w|ISO 15924}} script code. This is used when constructing HTML {{code|html|lang{{=}}}} tags. The {{lua|ietf_subtag}} value from the script's data file is used, if present; otherwise, the script code is used. For script codes which contain a hyphen, only the part after the hyphen is used (e.g. {{lua|"fa-Arab"}} becomes {{lua|"Arab"}}).]==] function Script:getIETFSubtag() local code = self._ietf_subtag if code == nil then code = self._data.ietf_subtag or match(self:getCode(), "[^%-]+$") self._ietf_subtag = code end return code end --[==[Returns a script object for the parent of the script, such as {"Arab"} for {"fa-Arab"}. It returns {nil} for scripts without a parent, like {"Latn"}, {"Grek"}, etc.]==] function Script:getParent() local parent = self._parentObject if parent == nil then parent = self:getParentCode() -- If the value is nil, it's cached as false. parent = parent and get_by_code(parent) or false self._parentObject = parent end return parent or nil end --[==[Returns the script code of the parent of the script, such as {"Arab"} for {"fa-Arab"}. It returns {nil} for scripts without a parent, like {"Latn"}, {"Grek"}, etc.]==] function Script:getParentCode() local parent = self._parentCode if parent == nil then -- If the value is nil, it's cached as false. parent = self._data.parent or false self._parentCode = parent end return parent or nil end function Script:getSystemCodes() if not self._systemCodes then local system_codes = self._data[3] if type(system_codes) == "table" then self._systemCodes = system_codes elseif type(system_codes) == "string" then self._systemCodes = split(system_codes, ",", true, true) else self._systemCodes = {} end end return self._systemCodes end function Script:getSystems() if not self._systemObjects then self._systemObjects = {} for _, system in ipairs(self:getSystemCodes()) do insert(self._systemObjects, get_writing_system(system)) end end return self._systemObjects end --[==[Check whether the script is of type `system`, which can be a writing system code or object. If multiple systems are passed, return true if the script is any of the specified systems.]==] function Script:isSystem(...) for _, system in ipairs{...} do if type(system) == "table" then system = system:getCode() end for _, s in ipairs(self:getSystemCodes()) do if system == s then return true end end end return false end --[==[Returns a table of types as a lookup table (with the types as keys). Currently, the only possible type is {script}.]==] function Script:getTypes() local types = self._types if types == nil then types = {script = true} local rawtypes = self._data.type if rawtypes then for t in gmatch(rawtypes, "[^,]+") do types[t] = true end end self._types = types end return types end --[==[Given a list of types as strings, returns true if the script has all of them. Use {{lua|hasType("script")}} to determine if an object that may be a language, family or script is a script.]==] function Script:hasType(...) Script.hasType = require(language_like_module).hasType return self:hasType(...) end --[==[ Return the name of the main category of that script. Example: {"Cyrillic script"} for Cyrillic, whose category is at [[:Category:Cyrillic script]]. Unless optional argument `nocap` is given, the script name at the beginning of the returned value will be capitalized. This capitalization is correct for category names, but not if the script name is lowercase and the returned value of this function is used in the middle of a sentence. (For example, the script with the code `Semap` has the name {"flag semaphore"}, which should remain lowercase when used as part of the category name [[:Category:Translingual letters in flag semaphore]] but should be capitalized in [[:Category:Flag semaphore templates]].) If you are considering using {getCategoryName("nocap")}, use {getDisplayForm()} instead. If the script has different names in different languages (e.g. `Aran`, which is called {"Shahmukhi"} in Punjabi and certain related languages but otherwise {"Arabic"}), and `lang` is given, return the language-specific name; otherwise return the default or only name. ]==] function Script:getCategoryName(nocap, lang) local name = self:getCanonicalName(lang) if category_name_has_suffix(name, scripts_suffixes or get_scripts_suffixes()) then name = name .. " script" end if not nocap then name = mw.getContentLanguage():ucfirst(name) end return name end --[==[ Return a link to the appropriate category for the script, displaying using the display form of the script. For example, `Cyrl` returns `[[:Category:Cyrillic script|Cyrillic script]]`. The display form is not automatically capitalized, so e.g. the script code `Semap` will return `[[:Category:Flag semaphore|flag semaphore]]`. If the script has different names in different languages (e.g. `Aran`, which is called {"Shahmukhi"} in Punjabi and certain related languages but otherwise {"Arabic"}), and `lang` is given, return the language-specific name; otherwise return the default or only name. ]==] function Script:makeCategoryLink(lang) return "[[:Category:" .. self:getCategoryName(lang) .. "|" .. self:getDisplayForm(lang) .. "]]" end --[==[Returns the Wikidata item id for the script or <code>nil</code>. This corresponds to the the second field in the data modules.]==] function Script:getWikidataItem() Script.getWikidataItem = require(language_like_module).getWikidataItem return self:getWikidataItem() end --[==[ Returns the name of the Wikipedia article for the script. `project` specifies the language and project to retrieve the article from, defaulting to {"enwiki"} for the English Wikipedia. Normally if specified it should be the project code for a specific-language Wikipedia e.g. "zhwiki" for the Chinese Wikipedia, but it can be any project, including non-Wikipedia ones. If the project is the English Wikipedia and the property {wikipedia_article} is present in the data module it will be used first. In all other cases, a sitelink will be generated from {:getWikidataItem} (if set). The resulting value (or lack of value) is cached so that subsequent calls are fast. If no value could be determined, and `noCategoryFallback` is {false}, {:getCategoryName} is used as fallback; otherwise, {nil} is returned. Note that if `noCategoryFallback` is {nil} or omitted, it defaults to {false} if the project is the English Wikipedia, otherwise to {true}. In other words, under normal circumstances, if the English Wikipedia article couldn't be retrieved, the return value will fall back to a link to the script's category, but this won't normally happen for any other project. ]==] function Script:getWikipediaArticle(noCategoryFallback, project) Script.getWikipediaArticle = require(language_like_module).getWikipediaArticle return self:getWikipediaArticle(noCategoryFallback, project) end --[==[Returns the name of the Wikimedia Commons category page for the script.]==] function Script:getCommonsCategory() Script.getCommonsCategory = require(language_like_module).getCommonsCategory return self:getCommonsCategory() end --[==[Returns the charset defining the script's characters from the script's data file. This can be used to search for words consisting only of this script, but see the warning above.]==] function Script:getCharacters() return self.characters or nil end --[==[Returns the number of characters in the text that are part of this script. '''Note:''' You should never assume that text consists entirely of the same script. Strings may contain spaces, punctuation and even wiki markup or HTML tags. HTML tags will skew the counts, as they contain Latin-script characters. So it's best to avoid them.]==] function Script:countCharacters(text) local charset = self._data.characters if charset == nil then return 0 end return select(2, ugsub(text, "[" .. charset .. "]", "")) end function Script:hasCapitalization() return not not self._data.capitalized end function Script:hasSpaces() return self._data.spaces ~= false end function Script:isTransliterated() return self._data.translit ~= false end --[==[Returns true if the script is (sometimes) sorted by scraping page content, meaning that it is sensitive to changes in capitalization during sorting.]==] function Script:sortByScraping() return not not self._data.sort_by_scraping end --[==[Returns the text direction. Horizontal scripts return {{lua|"ltr"}} (left-to-right) or {{lua|"rtl"}} (right-to-left), while vertical scripts return {{lua|"vertical-ltr"}} (vertical left-to-right) or {{lua|"vertical-rtl"}} (vertical right-to-left).]==] function Script:getDirection() return self._data.direction or "ltr" end function Script:getData() return self._data end --[==[Returns the name of the module containing the script's data. Currently, this is always [[Module:scripts/data]].]==] function Script:getDataModuleName() return scripts_data_module end --[==[Returns {{lua|true}} if the script contains characters that require fixes to Unicode normalization under certain circumstances, {{lua|false}} if it doesn't.]==] function Script:hasNormalizationFixes() return not not self._data.normalizationFixes end --[==[Corrects discouraged sequences of Unicode characters to the encouraged equivalents.]==] function Script:fixDiscouragedSequences(text) if self:hasNormalizationFixes() then local norm_fixes = self._data.normalizationFixes local to = norm_fixes.to if to then for i, v in ipairs(norm_fixes.from) do text = ugsub(text, v, to[i] or "") end end end return text end do local combining_classes -- Obtain the list of default combining classes. local function get_combining_classes() combining_classes, get_combining_classes = load_data(combining_classes_module), nil return combining_classes end -- Implements a modified form of Unicode normalization for instances where there are identified deficiencies in the default Unicode combining classes. local function fixNormalization(text, self) if not self:hasNormalizationFixes() then return text end local norm_fixes = self._data.normalizationFixes local new_classes = norm_fixes.combiningClasses if not (new_classes and umatch(text, "[" .. norm_fixes.combiningClassCharacters .. "]")) then return text end text = explode(text) -- Manual sort based on new combining classes. -- We can't use table.sort, as it compares the first/last values in an array as a shortcut, which messes things up. for i = 2, #text do local char = text[i] local class = new_classes[char] or (combining_classes or get_combining_classes())[char] if class then repeat i = i - 1 local prev = text[i] if (new_classes[prev] or (combining_classes or get_combining_classes())[prev] or 0) < class then break end text[i], text[i + 1] = char, prev until i == 1 end end return concat(text) end function Script:toFixedNFC(text) return fixNormalization(toNFC(text), self) end function Script:toFixedNFD(text) return fixNormalization(toNFD(text), self) end function Script:toFixedNFKC(text) return fixNormalization(toNFKC(text), self) end function Script:toFixedNFKD(text) return fixNormalization(toNFKD(text), self) end end function Script:toJSON(opts) local ret = { canonicalName = self:getCanonicalName(), canonicalNameTable = self:getCanonicalNameTable(), categoryName = self:getCategoryName("nocap"), code = self:getCode(), parent = self:getParentCode(), systems = self:getSystemCodes(), aliases = self:getAliases(), varieties = self:getVarieties(), otherNames = self:getOtherNames(), type = keys_to_list(self:getTypes()), direction = self:getDirection(), characters = self:getCharacters(), ietfSubtag = self:getIETFSubtag(), wikidataItem = self:getWikidataItem(), wikipediaArticle = self:getWikipediaArticle(true), } -- Use `deep_copy` when returning a table, so that there are no editing restrictions imposed by `mw.loadData`. return opts and opts.lua_table and deep_copy(ret) or to_json(ret, opts) end function export.makeObject(code, data) local data_type = type(data) if data_type ~= "table" then error(("bad argument #2 to 'makeObject' (table expected, got %s)"):format(data_type)) end return setmetatable({_data = data, _code = code, characters = data.characters}, Script) end make_object = export.makeObject local scripts_to_track = { ["fa-Arab"] = true, ["kk-Arab"] = true, ["ks-Arab"] = true, ["ku-Arab"] = true, ["ms-Arab"] = true, ["mzn-Arab"] = true, ["ota-Arab"] = true, ["pa-Arab"] = true, ["ps-Arab"] = true, ["sd-Arab"] = true, ["tt-Arab"] = true, ["ug-Arab"] = true, ["ur-Arab"] = true, } --[==[ Finds the script whose code matches the one provided. If it exists, it returns a {Script} object representing the script. Otherwise, it returns {nil}.]==] function export.getByCode(code) if scripts_to_track[code] then track(code) end local data = (scripts_data or get_scripts_data())[code] return data ~= nil and make_object(code, data) or nil end get_by_code = export.getByCode --[==[ Look for the script whose canonical name (the name used to represent that script on Wiktionary) matches the one provided. If it exists, it returns a {Script} object representing the script. Otherwise, it returns {nil}. The canonical name of scripts should always be unique (it is an error for two scripts on Wiktionary to share the same canonical name), so this is guaranteed to give at most one result.]==] function export.getByCanonicalName(name) if name == nil then return nil end local code = (scripts_canonical_names or get_scripts_canonical_names())[name] if code == nil then return nil end return get_by_code(code) end --[==[ Look for the script whose category name (the name used in categories for that script) matches the one provided. If it exists, it returns a {Script} object representing the script. Otherwise, it returns {nil}. In almost all cases, the category name for a script is its canonical name plus the word "script", e.g. "Cyrillic" has the category name "Cyrillic script". Where a canonical name ends with "script", "code" or "semaphore", the category name is identical to the canonical name. ]==] function export.getByCategoryName(name) if name == nil then return nil end local code, canonical_name = category_name_to_code( name, " script", scripts_canonical_names or get_scripts_canonical_names(), scripts_suffixes or get_scripts_suffixes() ) if code == nil then return nil, nil end return get_by_code(code), canonical_name end --[==[ Convert a canonical name to the corresponding category name. Unless optional argument `nocap` is given, the script name at the beginning of the returned value will be capitalized. See {:getCategoryName()} for more discussion. ]==] function export.canonicalNameToCategoryName(name, nocap) if category_name_has_suffix(name, scripts_suffixes or get_scripts_suffixes()) then name = name .. " script" end if not nocap then name = mw.getContentLanguage():ucfirst(name) end return name end --[==[ Takes a codepoint or a character and finds the script code (if any) that is appropriate for it based on the codepoint, using the data module [[Module:scripts/recognition data]]. The data module was generated from the patterns in [[Module:scripts/data]] using [[Module:User:Erutuon/script recognition]]. Converts the character to a codepoint. Returns a script code if the codepoint is in the list of individual characters, or if it is in one of the defined ranges in the 4096-character block that it belongs to, else returns "None". ]==] function export.charToScript(char) export.charToScript = require(scripts_chartoscript_module).charToScript return export.charToScript(char) end --[==[ Returns the code for the script that has the greatest number of characters in `text`. Useful for script tagging text that is unspecified for language. Uses [[Module:scripts/recognition data]] to determine a script code for a character language-agnostically. Specifically, it works as follows: Convert each character to a codepoint. Increment the counter for the script code if the codepoint is in the list of individual characters, or if it is in one of the defined ranges in the 4096-character block that it belongs to. Each script has a two-part counter, for primary and secondary matches. Primary matches are when the script is the first one listed; otherwise, it's a secondary match. When comparing scripts, first the total of both are compared (i.e. the overall number of matches). If these are the same, the number of primary and then secondary matches are used as tiebreakers. For example, this is used to ensure that `Grek` takes priority over `Polyt` if no characters which exclusively match `Polyt` are found, as `Grek` is a subset of `Polyt`. If `none_is_last_resort_only` is specified, this will never return {"None"} if any characters in `text` belong to a script. Otherwise, it will return {"None"} if there are more characters that don't belong to a script than belong to any individual script. (FIXME: This behavior is probably wrong, and `none_is_last_resort_only` should probably become the default.) ]==] function export.findBestScriptWithoutLang(text, none_is_last_resort_only) export.findBestScriptWithoutLang = require(scripts_chartoscript_module).findBestScriptWithoutLang return export.findBestScriptWithoutLang(text, none_is_last_resort_only) end return export io3j9hrdqyxoerkjf8e2y02nar9stid Module:languages/data/3/a 828 7873 54723 54391 2026-09-25T20:59:54Z Deadend0914 7211 54723 Scribunto text/plain local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["aaa"] = { "Ghotuo", 35463, "alv-yek", "Latn", } m["aab"] = { "Alumu-Tesu", 35034, "nic-alu", "Latn", } m["aac"] = { "Ari", 1811224, "ngf-gsu", "Latn", } m["aad"] = { "Amal", 56708, "paa-sep", "Latn", } -- "aae" is treated as "sq", see [[WT:LT]] m["aaf"] = { "Aranadan", 3507928, "dra-mal", "Mlym", -- Mlym translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } m["aag"] = { "Ambrak", 4741706, "paa-pal", "Latn", } m["aah"] = { "Abu'", 4670715, "paa-ara", "Latn", } m["aai"] = { "Arifama-Miniafia", 4790560, "poz-ocw", "Latn", } m["aak"] = { "Ankave", 3446690, "ngf-ata", "Latn", } m["aal"] = { "Afade", 56434, "cdc-cbm", "Latn", } m["aan"] = { "Anambé", 3507873, "tup-gua", "Latn", } m["aap"] = { "Arára (Pará)", 56807, "sai-pek", "Latn", } m["aaq"] = { "Penobscot", 3515185, "alg-abp", "Latn", } m["aas"] = { "Aasax", 56620, "cus-sou", "Latn", } -- "aat" is treated as "sq", see [[WT:LT]] m["aau"] = { "Abau", 3073568, "paa-sep", "Latn", } m["aaw"] = { "Solong", 7558834, "poz-ocw", "Latn", } m["aax"] = { "Upper Mandobo", 12636156, "ngf-dum", "Latn", } m["aaz"] = { "Amarasi", 4740192, "poz-tim", "Latn", } m["aba"] = { "Abé", 34833, "alv-lag", "Latn", } m["abb"] = { "Bankon", 34860, "bnt-bsa", "Latn", } m["abc"] = { "Ambala Ayta", 3448896, "phi", "Latn", } m["abd"] = { "Camarines Norte Agta", 3399682, "phi", "Latn", } m["abe"] = { "Abenaki", 17502788, "alg-abp", "Latn", } m["abf"] = { "Abai Sungai", 4663287, "poz-san", "Latn", } m["abg"] = { "Abaga", 3507954, "ngf-kya", "Latn", } m["abh"] = { "Tajiki Arabic", 56833, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["abi"] = { "Abidji", 34781, "alv-lag", "Latn", } m["abj"] = { "Aka-Bea", 2356391, "qfa-ads", "Latn", } m["abl"] = { "Abung", 49215, "poz-lgx", "Latn", } m["abm"] = { "Abanyom", 7502, "nic-eko", "Latn", } m["abn"] = { "Abua", 34835, "nic-cde", "Latn", } m["abo"] = { "Abon", 35121, "nic-tvn", "Latn", } m["abp"] = { "Abenlen Ayta", 3436621, "phi", "Latn", } m["abq"] = { "Abaza", 27567, "cau-abz", "Cyrl, Latn", translit = { Cyrl = "abq-translit" }, override_translit = true, display_text = { Cyrl = s["cau-Cyrl-displaytext"] }, strip_diacritics = { Cyrl = s["cau-Cyrl-stripdiacritics"], Latn = s["cau-Latn-stripdiacritics"], }, sort_key = { Cyrl = { from = { "гъв", "гъь", "гӏв", "джв", "джь", "къв", "къь", "кӏв", "кӏь", "хъв", "хӏв", "чӏв", -- 3 chars "гв", "гъ", "гь", "гӏ", "дж", "дз", "ё", "жв", "жь", "кв", "къ", "кь", "кӏ", "ль", "лӏ", "пӏ", "тл", "тш", "тӏ", "фӏ", "хв", "хъ", "хь", "хӏ", "цӏ", "чв", "чӏ", "шв", "шӏ" -- 2 chars }, to = { "г" .. p[3], "г" .. p[4], "г" .. p[7], "д" .. p[2], "д" .. p[3], "к" .. p[3], "к" .. p[4], "к" .. p[7], "к" .. p[8], "х" .. p[3], "х" .. p[6], "ч" .. p[3], "г" .. p[1], "г" .. p[2], "г" .. p[5], "г" .. p[6], "д" .. p[1], "д" .. p[4], "е" .. p[1], "ж" .. p[1], "ж" .. p[2], "к" .. p[1], "к" .. p[2], "к" .. p[5], "к" .. p[6], "л" .. p[1], "л" .. p[2], "п" .. p[1], "т" .. p[1], "т" .. p[2], "т" .. p[3], "ф" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[4], "х" .. p[5], "ц" .. p[1], "ч" .. p[1], "ч" .. p[2], "ш" .. p[1], "ш" .. p[2] } }, }, } -- "abr" Abron is treated as "ak" Akan, see [[WT:LT]] m["abs"] = { "Ambonese Malay", 3124354, "crp", "Latn", ancestors = "ms", } m["abt"] = { "Ambulas", 3508015, "paa-nnd", "Latn", } m["abu"] = { "Abure", 34767, "alv-ptn", "Latn", } m["abv"] = { "Baharna Arabic", 56576, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["abw"] = { "Pal", 7126121, "ngf-omo", "Latn", } m["abx"] = { "Inabaknon", 2820163, "poz-sbj", "Latn", } m["aby"] = { "Aneme Wake", 3508107, "ngf-yar", "Latn", } m["abz"] = { "Abui", 2822110, "paa-alp", "Latn", } m["aca"] = { "Achagua", 2822982, "awd", "Latn", } m["acb"] = { "Áncá", 11130787, "nic-mom", "Latn", } m["acd"] = { "Gikyode", 35256, "alv-gng", "Latn", } m["ace"] = { "Acehnese", 27683, "cmc", "Latn, Arab", standard_chars = { Latn = "AaBbCcDdEeÉéÈèËëFfGgHhIiJjKkLlMmNnOoÔôÖöPpQqRrSsTtUuVvWwXxYyZz", -- current orthography (not yet add Arab) c.punc }, } m["ach"] = { "Acholi", 34926, "sdv-los", "Latn", } m["aci"] = { "Aka-Cari", 2670418, "qfa-adn", "Latn", } m["ack"] = { "Aka-Kora", 3433680, "qfa-adn", "Latn", } m["acl"] = { "Akar-Bale", 3436825, "qfa-ads", "Latn", } m["acm"] = { "Iraqi Arabic", 56232, "sem-arb", "Arab, Hebr", strip_diacritics = { Arab = "ar-stripdiacritics", }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["acn"] = { "Achang", 56582, "tbq-brm", "Latn", } m["acp"] = { "Eastern Acipa", 5329945, "nic-kmk", "Latn", } m["acr"] = { "Achi", 34774, "myn", "Latn", } m["acs"] = { "Acroá", 2829146, "sai-cje", "Latn", } m["acu"] = { "Achuar", 2823170, "sai-jiv", "Latn", } m["acv"] = { "Achumawi", 56661, "nai-pal", "Latn", } m["acw"] = { "Hijazi Arabic", 56608, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["acx"] = { "Omani Arabic", 56630, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["acy"] = { "Cypriot Arabic", 56416, "sem-arb", "Latn, Grek", ancestors = "acm", strip_diacritics = { Latn = {remove_diacritics = c.grave .. c.acute .. c.breve}, }, -- Grek display_text, strip_diacritics, sort_key in [[Module:scripts/data]] standard_chars = { Latn = "AaBbCcDdΔδEeFfGgĠġĊċIiJjKkLlMmNnOoPpΘθRrSsTtUuVvWwXxYyZzŞş", c.punc }, } m["acz"] = { "Acheron", 34769, "alv-tal", "Latn", } m["ada"] = { "Adangme", 35141, "alv-gda", "Latn", } m["adb"] = { "Atauran", 125421255, "poz-cet", "Latn", } m["add"] = { "Dzodinka", 35266, "nic-nka", "Latn", } m["ade"] = { "Adele", 27740, "alv-ntg", "Latn", } m["adf"] = { "Dhofari Arabic", 56565, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["adg"] = { "Andegerebinha", 3508123, "aus-rnd", "Latn", } m["adh"] = { "Adhola", 1971400, "sdv-los", "Latn", } m["adi"] = { "Adi", 56440, "sit-tan", "Latn", } m["adj"] = { "Adioukrou", 34738, "alv-lag", "Latn", } m["adl"] = { "Galo", 2857892, "sit-tan", "Latn", } m["adn"] = { "Adang", 3398276, "paa-alp", "Latn", } m["ado"] = { "Abu", 56659, "paa-por", "Latn", } m["adp"] = { "Adap", 3512402, "sit-tib", "Tibt", ancestors = "dz", override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["adq"] = { "Adangbe", 34730, "alv-gda", "Latn", ancestors = "ada", } m["adr"] = { "Adonara", 4684505, "poz-cet", "Latn", } m["ads"] = { "Adamorobe Sign Language", 27709, "sgn", "Latn", -- when documented } m["adt"] = { "Adnyamathanha", 2225391, "aus-psw", "Latn", } m["adu"] = { "Aduge", 34734, "alv-nwd", "Latn", ancestors = "opa", } m["adw"] = { "Amondawa", 12626847, "tup-gua", "Latn", } m["ady"] = { "West Circassian", 27776, "cau-cir", "Cyrl, Latn, Arab", translit = { Cyrl = "cau-cir-translit", Arab = "ar-translit", }, override_translit = true, display_text = { Cyrl = s["cau-Cyrl-displaytext"] }, strip_diacritics = { Cyrl = s["cau-Cyrl-stripdiacritics"], Latn = s["cau-Latn-stripdiacritics"], }, sort_key = { Cyrl = { from = { "кхъу", "къӏу", -- 4 chars "гъу", "джу", "дзу", "жъу", "къу", "кхъ", "къӏ", "кӏу", "кӏь", "лъу", "лӏу", "пӏу", "сӏу", "тӏу", "фӏу", "хъу", "цӏу", "чъу", "чӏу", "шъу", "шӏу", "щӏу", -- 3 chars "гу", "гъ", "гь", "дж", "дз", "ё", "жъ", "жь", "ку", "къ", "кь", "кӏ", "лъ", "ль", "лӏ", "пӏ", "сӏ", "тӏ", "фӏ", "ху", "хъ", "хь", "цу", "цӏ", "чу", "чъ", "чӏ", "шъ", "шӏ", "щӏ", "ӏу", "ӏь" -- 2 chars }, to = { "к" .. p[5], "к" .. p[7], "г" .. p[3], "д" .. p[2], "д" .. p[4], "ж" .. p[2], "к" .. p[3], "к" .. p[4], "к" .. p[6], "к" .. p[10], "к" .. p[11], "л" .. p[2], "л" .. p[5], "п" .. p[2], "с" .. p[2], "т" .. p[2], "ф" .. p[2], "х" .. p[3], "ц" .. p[3], "ч" .. p[3], "ч" .. p[5], "ш" .. p[2], "ш" .. p[4], "щ" .. p[2], "г" .. p[1], "г" .. p[2], "г" .. p[4], "д" .. p[1], "д" .. p[3], "е" .. p[1], "ж" .. p[1], "ж" .. p[3], "к" .. p[1], "к" .. p[2], "к" .. p[8], "к" .. p[9], "л" .. p[1], "л" .. p[3], "л" .. p[4], "п" .. p[1], "с" .. p[1], "т" .. p[1], "ф" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[4], "ц" .. p[1], "ц" .. p[2], "ч" .. p[1], "ч" .. p[2], "ч" .. p[4], "ш" .. p[1], "ш" .. p[3], "щ" .. p[1], "ӏ" .. p[1], "ӏ" .. p[2] } }, }, } m["adz"] = { "Adzera", 3327445, "poz-ocw", "Latn", } m["aea"] = { "Areba", 3509129, "aus-pam", "Latn", } m["aeb"] = { "Tunisian Arabic", 56240, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["aed"] = { "Argentine Sign Language", 3322073, "sgn", "Latn", -- when documented } m["aee"] = { "Northeast Pashayi", 12642198, "inc-pas", "Arab, Latn", } m["aek"] = { "Haeke", 5638166, "poz-cln", "Latn", } m["ael"] = { "Ambele", 34818, "nic-grf", "Latn", } m["aem"] = { "Arem", 3507920, "mkh-vie", "Latn", } m["aen"] = { "Armenian Sign Language", 3446604, "sgn", } m["aeq"] = { "Aer", 3246741, "inc-wes", "Arab", } m["aer"] = { "Eastern Arrernte", 10728232, "aus-rnd", "Latn", } m["aes"] = { "Alsea", 2395641, nil, "Latn", } m["aeu"] = { "Akeu", 4700657, "tbq-sil", "Latn", } m["aew"] = { "Ambakich", 56642, "paa-eke", "Latn", } m["aey"] = { "Amele", 3508025, "ngf-gum", "Latn", } m["aez"] = { "Aeka", 16110528, "ngf-oro", "Latn", } m["afb"] = { "Gulf Arabic", 56385, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["afd"] = { "Andai", 4753480, "paa-arf", "Latn", } m["afe"] = { "Putukwam", 3914930, "nic-ben", "Latn", } m["afg"] = { "Afghan Sign Language", 4689093, "sgn", } m["afh"] = { "Afrihili", 384707, "art", "Latn", type = "appendix-constructed", } m["afi"] = { "Akrukay", 57003, "paa-tam", "Latn", } m["afk"] = { "Nanubae", 6964416, "paa-arf", "Latn", } m["afn"] = { "Defaka", 35174, "nic", "Latn", } m["afo"] = { "Eloyi", 3914066, "nic-plt", "Latn", } m["afp"] = { "Tapei", 16887371, "paa-arf", "Latn", } m["afs"] = { "Afro-Seminole Creole", 27867, "crp", "Latn", ancestors = "en", } m["aft"] = { "Afitti", 3400829, "sdv-nyi", "Latn", } m["afu"] = { "Awutu", 34847, "alv-gng", "Latn", } m["afz"] = { "Obokuitai", 7075258, "paa-clp", "Latn", } m["agb"] = { "Legbo", 35584, "nic-uce", "Latn", } m["agc"] = { "Agatu", 34732, "alv-ido", "Latn", } m["agd"] = { "Agarabi", 3399642, "ngf-gau", "Latn", } m["age"] = { "Angal", 10951553, "ngf-ank", "Latn", } m["agf"] = { "Arguni", 12473346, "poz-cet", "Latn", } m["agg"] = { "Angor", 3508100, "paa-sng", "Latn", } m["agh"] = { "Ngelima", 7022266, "bnt-bta", "Latn", } m["agj"] = { "Argobba", 29292, "sem-eth", "Ethi", } m["agk"] = { "Isarog Agta", 6078982, "phi", "Latn", } m["agl"] = { "Fembe", 372927, "ngf-est", "Latn", } m["agm"] = { "Angaataha", 3508001, "ngf-ang", "Latn", } m["agn"] = { "Agutaynen", 3399717, "phi-kal", "Latn", } m["ago"] = { "Tainae", 7676186, "ngf-taa", "Latn", } m["agq"] = { "Aghem", 34737, "nic-rnw", "Latn", } m["agr"] = { "Aguaruna", 1526530, "sai-jiv", "Latn", } m["ags"] = { "Esimbi", 35260, "nic-bds", "Latn", } m["agt"] = { "Central Cagayan Agta", 5017296, "phi", "Latn", } m["agu"] = { "Aguacateca", 35091, "myn", "Latn", } m["agv"] = { "Remontado Agta", 3508085, "phi", "Latn", } m["agw"] = { "Kahua", 3191906, "poz-sls", "Latn", } m["agx"] = { "Aghul", 36498, "cau-esm", "Cyrl", translit = "cau-nec-translit", override_translit = true, display_text = s["cau-Cyrl-displaytext"], strip_diacritics = s["cau-Cyrl-stripdiacritics"], sort_key = { from = {"аь", "гъ", "гь", "гӏ", "дж", "ё", "къ", "кь", "кӏ", "оь", "пӏ", "тӏ", "уь", "хъ", "хь", "хӏ", "цӏ", "чӏ"}, to = {"а" .. p[1], "г" .. p[1], "г" .. p[2], "г" .. p[3], "д" .. p[1], "е" .. p[1], "к" .. p[1], "к" .. p[2], "к" .. p[3], "о" .. p[1], "п" .. p[1], "т" .. p[1], "у" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[3], "ц" .. p[1], "ч" .. p[1]} }, } m["agy"] = { "Southern Alta", 7569611, "phi", "Latn", } m["agz"] = { "Mount Iriga Agta", 6921432, "phi", "Latn", } m["aha"] = { "Ahanta", 34729, "alv-ctn", "Latn", } m["ahb"] = { "Axamb", 2874710, "poz-vnc", "Latn", } m["ahg"] = { "Qimant", 35663, "cus-cen", "Latn", } m["ahh"] = { "Aghu", 3436645, "ngf-awy", "Latn", } m["ahi"] = { "Tiagba", 3400073, "kro-aiz", "Latn", } m["ahk"] = { "Akha", 56643, "tbq-han", "Latn, Mymr, Thai", sort_key = { Thai = { from = {"[%pๆ]", "[็-๎]", "([เแโใไ])([ก-ฮ])"}, to = {"", "", "%2%1"} }, }, } m["ahl"] = { "Igo", 35412, "alv-ktg", "Latn", } m["ahm"] = { "Mobu", 35967, "kro-aiz", "Latn", } m["ahn"] = { "Àhàn", 34723, "alv-aah", "Latn", } m["aho"] = { "Ahom", 34778, "tai-swe", "Ahom", translit = "Ahom-translit", } m["ahp"] = { "Apro", 34810, "alv-kwa", "Latn", } m["ahr"] = { "Ahirani", 15549890, "raj", "Deva", translit = "mr-translit", } m["ahs"] = { "Ashe", 34823, "nic-plc", "Latn", } m["aht"] = { "Ahtna", 21058, "ath-nor", "Latn", } m["aia"] = { "Arosi", 2863483, "poz-sls", "Latn", } m["aib"] = { "Äynu", 27927, "qfa-mix", "Arab, Latn", ancestors = "ug, fa" } m["aic"] = { "Ainbai", 3332149, "paa-bew", "Latn", } m["aid"] = { "Alngith", 3279409, "aus-pmn", "Latn", } m["aie"] = { "Amara", 2841180, "poz-ocw", "Latn", } m["aif"] = { "Agi", 3331491, "paa-wpa", "Latn", } m["aig"] = { "Antigua and Barbuda Creole English", 3244184, "crp", "Latn", ancestors = "en", } m["aih"] = { "Ai-Cham", 2827749, "qfa-kms", "Latn, Hani", sort_key = { Hani = "Hani-sortkey" }, } m["aii"] = { "Assyrian Neo-Aramaic", 29440, "sem-nna", "Syrc", translit = "aii-translit", strip_diacritics = "Syrc-stripdiacritics", } m["aij"] = { "Lishanid Noshan", 3436467, "sem-nna", "Hebr", -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["aik"] = { "Ake", 34808, "nic-pls", "Latn", } m["ail"] = { "Aimele", 3327418, "ngf-bos", "Latn", } m["aim"] = { "Aimol", 4697175, "tbq-kuk", "Latn, Beng", } m["ain"] = { "Ainu", 27969, "qfa-ain", "Kana, Latn, Cyrl", sort_key = { Kana = "Kana-sortkey" }, } m["aio"] = { "Aiton", 3399725, "tai-swe", "Mymr", translit = "aio-phk-translit", display_text = s["aio-displaytext"], strip_diacritics = s["aio-stripdiacritics"], } m["aip"] = { "Burumakok", 5000984, "ngf-wok", "Latn", } m["air"] = { "Airoran", 3321131, "paa-saa", "Latn", } m["ait"] = { "Arikem", 3446679, "tup", "Latn", } m["aiw"] = { "Aari", 7495, "omv-aro", "Latn", } m["aix"] = { "Aighon", 3504287, "poz-ocw", "Latn", } m["aiy"] = { "Ali", 34814, "gba-eas", "Latn", } m["aja"] = { "Aja (East Africa)", 3237491, "csu-bkr", "Latn", } m["ajg"] = { "Aja (West Africa)", 35035, "alv-gbe", "Latn", } m["aji"] = { "Ajië", 2828867, "poz-cln", "Latn", } m["ajn"] = { "Andajin", 16111302, "aus-wor", "Latn", } m["ajp"] = { "South Levantine Arabic", 55633582, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["ajs"] = { "Algerian Jewish Sign Language", 5555709, "sgn", } -- m["ajt"] Judeo-Tunisian Arabic now included in Tunisian Arabic -- m["aju"] Judeo-Moroccan Arabic now included in Moroccan Arabic m["ajw"] = { "Ajawa", 56645, "cdc-wst", "Latn", } m["ajz"] = { "Amri Karbi", 3508092, "tbq-kuk", "Latn", ancestors = "mjw", } m["akb"] = { "Angkola Batak", 2640686, "btk", "Latn, Batk", } m["akc"] = { "Mpur", 3327139, "qfa-iso", -- Papuan; based on Palmer (2018), Ethnologue and Glottolog "Latn", } m["akd"] = { "Ukpet-Ehom", 36618, "nic-ucr", "Latn", } m["ake"] = { "Akawaio", 28059, "sai-pem", "Latn", } m["akf"] = { "Akpa", 34801, "alv-ido", "Latn", } m["akg"] = { "Anakalangu", 4750964, "poz-cet", "Latn", } m["akh"] = { "Angal Heneng", 10950354, "ngf-ank", "Latn", } m["aki"] = { "Aiome", 56735, "paa-aia", "Latn", } m["akj"] = { "Jeru", 2919121, "qfa-adn", "Latn, Deva", } m["akk"] = { "Akkadian", 35518, "sem-eas", "Xsux, Latn", } m["akl"] = { "Aklanon", 8773, "phi", "Latn", } m["akm"] = { "Aka-Bo", 35361, "qfa-adn", "Latn", } m["ako"] = { "Akurio", 56650, "sai-tar", "Latn", } m["akp"] = { "Siwu", 36470, "alv-ntg", "Latn", } m["akq"] = { "Ak", 56654, "paa-sep", "Latn", } m["akr"] = { "Araki", 2699882, "poz-vnn", "Latn", } m["aks"] = { "Akaselem", 34817, "nic-grm", "Latn", } m["akt"] = { "Akolet", 3330162, "poz-ocw", "Latn", } m["aku"] = { "Akum", 34799, "nic-ykb", "Latn", } m["akv"] = { "Akhvakh", 56423, "cau-and", "Cyrl", translit = "cau-nec-translit", override_translit = true, display_text = s["cau-Cyrl-displaytext"], strip_diacritics = s["cau-Cyrl-stripdiacritics"], } m["akw"] = { "Akwa", 34802, "bnt-mbo", "Latn", } m["akx"] = { "Aka-Kede", 3436816, "qfa-adc", "Latn", } m["aky"] = { "Aka-Kol", 3436784, "qfa-adc", "Latn", } m["akz"] = { "Alabama", 1815020, "nai-mus", "Latn", } m["ala"] = { "Alago", 34813, "alv-ido", "Latn", } m["alc"] = { "Kawésqar", 56544, "aqa", "Latn", } m["ald"] = { "Alladian", 34837, "alv-lag", "Latn", } m["ale"] = { "Aleut", 27210, "esx", "Latn, Cyrl", } m["alf"] = { "Alege", 34815, "nic-ben", "Latn", } m["alh"] = { "Alawa", 2147917, "aus-gun", "Latn", } m["ali"] = { "Amaimon", 3327427, "ngf-mad", "Latn", } m["alj"] = { "Alangan", 3327423, "phi", "Latn", } m["alk"] = { "Alak", 2714690, "mkh", "Latn", } m["all"] = { "Allar", 3393634, "dra-mal", "Mlym", -- Mlym translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } -- "aln" is treated as "sq", see [[WT:LT]] m["alm"] = { "Amblong", 11022615, "poz-vnn", "Latn", } m["alo"] = { "Larike-Wakasihu", 3217929, "poz-cma", "Latn", } m["alp"] = { "Alune", 3327367, "poz-cet", "Latn", } m["alq"] = { "Algonquin", 28092, "alg", "Latn, Cans", ancestors = "oj", } m["alr"] = { "Alutor", 28213, "qfa-ckn", "Cyrl", strip_diacritics = { from = {"['’]"}, to = {"ʼ"} }, sort_key = { from = {"вʼ", "гʼ", "ғ", "ә", "ё", "ӄ", "ӈ"}, to = {"в" .. p[1], "г" .. p[1], "г" .. p[2], "е" .. p[1], "е" .. p[2], "к" .. p[1], "н" .. p[1]} }, } m["alt"] = { "Southern Altai", 1991779, "trk-kkp", "Cyrl", translit = "Altai-translit", sort_key = { from = {"ј", "ё", "ҥ", "ӧ", "ӱ"}, to = {"д" .. p[1], "е" .. p[1], "н" .. p[1], "о" .. p[1], "у" .. p[1]} }, } m["alu"] = { "'Are'are", 5160, "poz-sls", "Latn", } m["alw"] = { "Alaba", 56652, "cus-hec", "Latn", } m["alx"] = { "Amol", 3504260, "paa-pal", "Latn", } m["aly"] = { "Alyawarr", 3327389, "aus-rnd", "Latn", } m["alz"] = { "Alur", 56507, "sdv-los", "Latn", } m["ama"] = { "Amanayé", 3508053, "tup-gua", "Latn", } m["amb"] = { "Ambo", 3450142, "nic-tvn", "Latn", } m["amc"] = { "Amahuaca", 2669150, "sai-pan", "Latn", } m["ame"] = { "Yanesha'", 3088540, "awd", "Latn", } m["amf"] = { "Hamer-Banna", 35764, "omv-aro", "Latn, Ethi", sort_key = "amf-utilities" } m["amg"] = { "Amurdag", 3360016, "aus-wdj", "Latn", } m["ami"] = { "Amis", 35132, "map", "Latn", } m["amj"] = { "Amdang", 28335, "ssa-fur", "Latn", } m["amk"] = { "Ambai", 1875885, "poz-hce", "Latn", } m["aml"] = { "War-Jaintia", 56321, "aav-khs", "Latn", } m["amm"] = { "Ama", 3446626, "paa-lma", "Latn", } m["amn"] = { "Amanab", 3327399, "paa-war", "Latn", } m["amo"] = { "Amo", 34826, "nic-kne", "Latn", } m["amp"] = { "Alamblak", 56688, "paa-sep", "Latn", } m["amq"] = { "Amahai", 3327384, "poz-cma", "Latn", } m["amr"] = { "Amarakaeri", 35128, "sai-har", "Latn", } m["ams"] = { "Southern Amami Ōshima", 2840986, "jpx-nry", "Jpan", translit = s["jpx-translit"], display_text = s["jpx-displaytext"], strip_diacritics = s["jpx-stripdiacritics"], sort_key = s["jpx-sortkey"], } m["amt"] = { "Amto", 56517, "paa-amu", "Latn", } m["amu"] = { "Guerrero Amuzgo", 3501942, "omq", "Latn", } m["amv"] = { "Ambelau", 2669214, "poz-cma", "Latn", } m["amw"] = { "Western Neo-Aramaic", 34226, "sem-arw", "Armi, Syrc, Latn", strip_diacritics = { Syrc = "Syrc-stripdiacritics" }, } m["amx"] = { "Anmatyerre", 10412317, "aus-rnd", "Latn", } m["amy"] = { "Ami", 10408315, "aus-dal", "Latn", } m["amz"] = { "Atampaya", 3446651, "aus-pam", "Latn", } m["ana"] = { "Andaqui", 2846078, nil, "Latn", } m["anb"] = { "Andoa", 2846171, "sai-zap", "Latn", } m["anc"] = { "Ngas", 35999, "cdc-wst", "Latn", } m["and"] = { "Ansus", 3513300, "poz-hce", "Latn", } m["ane"] = { "Xârâcùù", 3571097, "poz-cln", "Latn", } m["anf"] = { "Animere", 34783, "alv-ktg", "Latn", } m["ang"] = { "Old English", 42365, "gmw-ang", "Latn, Runr", translit = { Runr = "Runr-translit" }, strip_diacritics = { Latn = { remove_diacritics = c.acute .. c.circ .. c.macron .. c.breve .. c.dotabove .. c.diaer .. c.dotbelow, from = {"[Ƿƿ]"}, to = {{ ["Ƿ"] = "W", ["ƿ"] = "w", }}, }, }, sort_key = { Latn = { remove_diacritics = c.acute .. c.circ .. c.macron .. c.breve .. c.dotabove .. c.diaer .. c.dotbelow, from = {"[æƀꝺðꝼᵹȝłœꞃꞅꞇþꝥꝧƿ]"}, to = {{ ["æ"] = "ae", ["ƀ"] = "b", ["ꝺ"] = "d", ["ð"] = "d" .. p[1], ["ꝼ"] = "f", ["ᵹ"] = "g", ["ȝ"] = "g" .. p[1], ["ł"] = "l", ["œ"] = "oe", ["ꞃ"] = "r", ["ꞅ"] = "s", ["ꞇ"] = "t", ["þ"] = "t" .. p[1], ["ꝥ"] = "t" .. p[1], ["ꝧ"] = "t" .. p[1], ["ƿ"] = "w", }}, }, }, standard_chars = { Latn = "AaÆæBbCcDdÐðEeFfGgHhIiLlMmNnOoŒœPpRrSsTtÞþUuWwXxYy", c.punc, }, } m["anh"] = { "Nend", 6991554, "ngf-wso", "Latn", } m["ani"] = { "Andi", 34849, "cau-and", "Cyrl", translit = "cau-nec-translit", override_translit = true, display_text = s["cau-Cyrl-displaytext"], strip_diacritics = s["cau-Cyrl-stripdiacritics"], } m["anj"] = { "Anor", 56458, "paa-aia", "Latn", } m["ank"] = { "Goemai", 35272, "cdc-wst", "Latn", } m["anl"] = { "Anu", 4777679, "sit-mru", "Latn", } m["anm"] = { "Anāl", 56235, "tbq-kuk", "Latn", } m["ann"] = { "Obolo", 36614, "nic-lcr", "Latn", } m["ano"] = { "Andoque", 2669225, "qfa-iso", "Latn", } m["anp"] = { "Angika", 28378, "inc-bih", "Deva, Kthi", translit = { Deva = "hi-translit", Kthi = "bho-Kthi-translit", }, } m["anq"] = { "Jarawa", 2475526, "qfa-ong", "Latn", } m["anr"] = { "Andh", 4754314, "inc-sou", "Deva", } m["ans"] = { "Anserma", 3446613, "sai-chc", "Latn", } m["ant"] = { "Antakarinya", 921304, "aus-psw", "Latn", } m["anu"] = { "Anuak", 56677, "sdv-lon", "Latn", } m["anv"] = { "Denya", 35187, "nic-mam", "Latn", } m["anw"] = { "Anaang", 2845320, "nic-ief", "Latn", } m["anx"] = { "Andra-Hus", 2846195, "poz-aay", "Latn", } m["any"] = { "Anyi", 28395, "alv-ctn", "Latn", } m["anz"] = { "Anem", 56512, "qfa-dis", -- Papuan; might be an isolate or in a putative West New Britain family "Latn", } m["aoa"] = { "Angolar", 34994, "crp", "Latn", ancestors = "pt", } m["aob"] = { "Abom", 3446647, "qfa-dis", -- Papuan; possibly a divergent Tirio language (Anim family), or a top-level TNG node "Latn", } m["aoc"] = { "Pemon", 10729616, "sai-pem", "Latn", } m["aod"] = { "Andarum", 3507888, "paa-ata", "Latn", } m["aoe"] = { "Angal Enen", 10951638, "ngf-ank", "Latn", } m["aof"] = { "Bragat", 3507977, "paa-pal", "Latn", } m["aog"] = { "Angoram", 56366, -- cf 6754745 for merged dialect "paa-lse", "Latn", } m["aoi"] = { "Anindilyakwa", 2714654, "aus-arn", "Latn", } m["aoj"] = { "Mufian", 3507881, "paa-ara", "Latn", } m["aok"] = { "Arhö", 4790086, "poz-cln", "Latn", } m["aol"] = { "Alorese", 3332062, "poz", "Latn", } m["aom"] = { "Ömie", 8078975, "ngf-koi", "Latn", } m["aon"] = { "Bumbita Arapesh", 3508044, "paa-ara", "Latn", } m["aor"] = { "Aore", 12627129, "poz-vnn", "Latn", } m["aos"] = { "Taikat", 7676018, "paa-taa", "Latn", } m["aot"] = { "Atong (India)", 5646, "tbq-bdg", "Latn, Beng", } m["aou"] = { "A'ou", 16109994, "gio", "Latn", -- also Hani? } m["aox"] = { "Atorada", 3507932, "awd", "Latn", } m["aoz"] = { "Uab Meto", 3441962, "poz-tim", "Latn", } m["apb"] = { "Sa'a", 36294, "poz-sls", "Latn", } m["apc"] = { "North Levantine Arabic", 22809485, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["apd"] = { "Sudanese Arabic", 56573, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["ape"] = { "Bukiyip", 3507895, "paa-ara", "Latn", } m["apf"] = { "Pahanan Agta", 7135432, "phi", "Latn", } m["apg"] = { "Ampanang", 4748035, "poz", "Latn", } m["aph"] = { "Athpare", 3449126, "sit-kie", "Deva, Latn", } m["api"] = { "Apiaká", 3507941, "tup-gua", "Latn", } m["apj"] = { "Jicarilla", 28277, "apa", "Latn", } m["apk"] = { "Plains Apache", 27861, "apa", "Latn", } m["apl"] = { "Lipan", 28269, "apa", "Latn", } m["apm"] = { "Chiricahua", 13368, "apa", "Latn", } m["apn"] = { "Apinayé", 2858311, "sai-nje", "Latn", } m["apo"] = { "Ambul", 12627135, "poz-ocw", "Latn", } m["app"] = { "Apma", 2669188, "poz-vnn", "Latn", } m["apq"] = { "A-Pucikwar", 28466, "qfa-adc", "Latn", } m["apr"] = { "Arop-Lokep", 2863482, "poz-ocw", "Latn", } m["aps"] = { "Arop-Sissano", 12627242, "poz-ocw", "Latn", } m["apt"] = { "Apatani", 56306, "sit-tan", "Latn", } m["apu"] = { "Apurinã", 2859081, "awd", "Latn", } m["apv"] = { "Alapmunte", 16110782, "sai-nmk", "Latn", } m["apw"] = { "Western Apache", 28060, "apa", "Latn", } m["apx"] = { "Aputai", 12473343, "poz-tim", "Latn", } m["apy"] = { "Apalaí", 2736980, "sai-gui", "Latn", } m["apz"] = { "Safeyoka", 7398693, "ngf-woj", "Latn", } m["aqc"] = { "Archi", 34915, "cau-lzg", "Cyrl", translit = "cau-nec-translit", override_translit = true, display_text = s["cau-Cyrl-displaytext"], strip_diacritics = s["cau-Cyrl-stripdiacritics"], sort_key = { from = { "ккъӏв", "ххьӏв", -- 5 chars "гъӏв", "ёоӏ", "ккъӏ", "ккъв", "къӏв", "ллъв", "ххьӏ", "хъӏв", "хьӏв", "ццӏв", "ччӏв", -- 4 chars "ааӏ", "гӏв", "гъӏ", "гъв", "гьв", "ееӏ", "ёӏ", "ёо", "ииӏ", "кӏв", "ккв", "ккъ", "къӏ", "къв", "кьв", "лӏв", "ллъ", "лъв", "льв", "ооӏ", "пӏв", "ппв", "ссв", "тӏв", "ттв", "ууӏ", "хӏв", "ххв", "хъӏ", "хъв", "хьӏ", "цӏв", "ццӏ", "ццв", "чӏв", "ччӏ", "ээӏ", "юуӏ", "яаӏ", -- 3 chars "аӏ", "аа", "гӏ", "гв", "гъ", "гь", "дв", "еӏ", "ее", "ё", "жв", "зв", "иӏ", "ии", "кӏ", "кв", "кк", "къ", "кь", "лӏ", "лв", "лъ", "ль", "оӏ", "оо", "пӏ", "пв", "пп", "св", "сс", "тӏ", "тв", "тт", "уӏ", "уу", "фв", "хӏ", "хв", "хх", "хъ", "цӏ", "цв", "цц", "чӏ", "чв", "шв", "щв", "эӏ", "ээ", "юӏ", "юу", "яӏ", "яа" -- 2 chars }, to = { "к" .. p[8], "х" .. p[7], "г" .. p[6], "е" .. p[7], "к" .. p[7], "к" .. p[9], "к" .. p[12], "л" .. p[5], "х" .. p[6], "х" .. p[10], "х" .. p[13], "ц" .. p[6], "ч" .. p[5], "а" .. p[3], "г" .. p[2], "г" .. p[5], "г" .. p[7], "г" .. p[9], "е" .. p[3], "е" .. p[5], "е" .. p[6], "и" .. p[3], "к" .. p[2], "к" .. p[5], "к" .. p[6], "к" .. p[11], "к" .. p[13], "к" .. p[15], "л" .. p[2], "л" .. p[4], "л" .. p[7], "л" .. p[9], "о" .. p[3], "п" .. p[2], "п" .. p[5], "с" .. p[3], "т" .. p[2], "т" .. p[5], "у" .. p[3], "х" .. p[2], "х" .. p[5], "х" .. p[9], "х" .. p[11], "х" .. p[12], "ц" .. p[2], "ц" .. p[5], "ц" .. p[7], "ч" .. p[2], "ч" .. p[4], "э" .. p[3], "ю" .. p[3], "я" .. p[3], "а" .. p[1], "а" .. p[2], "г" .. p[1], "г" .. p[3], "г" .. p[4], "г" .. p[8], "д" .. p[1], "е" .. p[1], "е" .. p[2], "е" .. p[4], "ж" .. p[1], "з" .. p[1], "и" .. p[1], "и" .. p[2], "к" .. p[1], "к" .. p[3], "к" .. p[4], "к" .. p[10], "к" .. p[14], "л" .. p[1], "л" .. p[3], "л" .. p[6], "л" .. p[8], "о" .. p[1], "о" .. p[2], "п" .. p[1], "п" .. p[3], "п" .. p[4], "с" .. p[1], "с" .. p[2], "т" .. p[1], "т" .. p[3], "т" .. p[4], "у" .. p[1], "у" .. p[2], "ф" .. p[1], "х" .. p[1], "х" .. p[3], "х" .. p[4], "х" .. p[8], "ц" .. p[1], "ц" .. p[3], "ц" .. p[4], "ч" .. p[1], "ч" .. p[3], "ш" .. p[1], "щ" .. p[1], "э" .. p[1], "э" .. p[2], "ю" .. p[1], "ю" .. p[2], "я" .. p[1], "я" .. p[2] } }, } m["aqd"] = { "Ampari Dogon", 4748057, "nic-dgw", "Latn", } m["aqg"] = { "Arigidi", 34829, "alv-von", "Latn", } m["aqm"] = { "Atohwaim", 11732297, "paa-kay", "Latn", } m["aqn"] = { "Northern Alta", 7058116, "phi", "Latn", } m["aqp"] = { "Atakapa", 10975683, "qfa-iso", "Latn", } m["aqr"] = { "Arhâ", 4790085, "poz-cln", "Latn", } m["aqt"] = { "Angaité", 15736037, "sai-mas", "Latn", } m["aqz"] = { "Akuntsu", 4701960, "tup", "Latn", } m["arc"] = { "Aramaic", 28602, "sem-ara", "Hebr, Armi, Syrc, Palm, Nbat, Phnx, Mand, Samr, Hatr, Elym", translit = { Armi = "Armi-translit", Palm = "Palm-translit", }, strip_diacritics = { -- The first three were added by [[User:Wikitiki89]] in 2015 for use with Syriac, which has diacritics that look -- like a diaeresis (syāmē) and macrons above and below (mṭalqānā); see Wikipedia [[w:Syriac alphabet]]. But -- I don't know if they are actually represented using these diacritics. Syrc = {remove_diacritics = c.macron .. c.diaer .. c.macronbelow .. u(0x0730) .. "-" .. u(0x0748)}, }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] -- Samr strip_diacritics, sort_key in [[Module:scripts/data]]; previously no sort_key for Samr, presumably a mistake -- Phnx translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } m["ard"] = { "Arabana", 3507959, "aus-kar", "Latn", } m["are"] = { "Western Arrernte", 12645549, "aus-rnd", "Latn", } m["arh"] = { "Arhuaco", 2640621, "cba", "Latn", } m["ari"] = { "Arikara", 56539, "cdd", "Latn", strip_diacritics = {remove_diacritics = c.acute}, } m["arj"] = { "Arapaso", 9627356, "sai-tuc", "Latn", } m["ark"] = { "Arikapú", 3446640, "sai-mje", "Latn", } m["arl"] = { "Arabela", 2591221, "sai-zap", "Latn", } m["arn"] = { "Mapudungun", 33730, "sai-ara", "Latn", } m["aro"] = { "Araona", 958414, "sai-tac", "Latn", } m["arp"] = { "Arapaho", 56417, "alg-ara", "Latn", } m["arq"] = { "Algerian Arabic", 56499, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["arr"] = { "Arara-Karo", 35539, "tup", "Latn", } m["ars"] = { "Najdi Arabic", 56574, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["aru"] = { "Arua", 2746221, "auf", "Latn", } m["arv"] = { "Arbore", 56883, "cus-eas", "Latn", } m["arw"] = { "Lokono", 2655664, "awd-taa", "Latn", } m["arx"] = { "Aruá", 3507907, "tup", "Latn", } m["ary"] = { "Moroccan Arabic", 56426, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["arz"] = { "Egyptian Arabic", 29919, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["asa"] = { "Pare", 36403, "bnt-par", "Latn", } m["asb"] = { "Assiniboine", 2591288, "sio-dkt", "Latn", } m["asc"] = { "Casuarina Coast Asmat", 11732046, "ngf-asm", "Latn", } m["ase"] = { "American Sign Language", 14759, "sgn-asl", "Sgnw", } m["asf"] = { "Auslan", 29525, "sgn", "Latn", -- when documented } m["asg"] = { "Cishingini", 35199, "nic-kam", "Latn", } m["ash"] = { "Abishira", 2871740, "qfa-dis", -- extinct, poorly documented; isolate or in a proposed Tequiraca-Canichana family by Kaufman (1994) "Latn", } m["asi"] = { "Buruwai", 5001031, "ngf-sab", "Latn", } m["asj"] = { "Nsari", 36418, "nic-bbe", "Latn", } m["ask"] = { "Ashkun", 29379, "nur-sou", "Arab, Latn", } m["asl"] = { "Asilulu", 12473347, "poz-cma", "Latn", } m["asn"] = { "Xingú Asuriní", 8044571, "tup-gua", "Latn", } m["aso"] = { "Dano", 5220979, "ngf-gah", "Latn", } m["asp"] = { "Algerian Sign Language", 3135421, "sgn", } m["asq"] = { "Austrian Sign Language", 36668, "sgn", "Latn", -- when documented } m["asr"] = { "Asuri", 3504321, "mun", "Latn", -- when documented } m["ass"] = { "Ipulo", 35408, "nic-tvc", "Latn", } m["ast"] = { "Asturian", 29507, "roa-asl", "Latn", } m["asu"] = { "Tocantins Asurini", 32041490, "tup-gua", "Latn", } m["asv"] = { "Asoa", 56296, "csu-maa", "Latn", } m["asw"] = { "Australian Aboriginal Sign Language", 955216, "sgn", "Latn", -- when documented } m["asx"] = { "Muratayak", 11732766, "ngf-war", "Latn", } m["asy"] = { "Yaosakor Asmat", 16113158, "ngf-asm", "Latn", } m["asz"] = { "As", 2866218, "poz-hce", "Latn", } m["ata"] = { "Pele-Ata", 56511, "qfa-dis", -- Papuan; possibly in a putative West New Britain family, or an isolate "Latn", } m["atb"] = { "Zaiwa", 56594, "tbq-brm", "Latn, Lisu", -- also Hani? -- Lisu translit, sort_key in [[Module:scripts/data]] } m["atc"] = { "Atsahuaca", 4817730, "sai-pan", "Latn", } m["atd"] = { "Ata Manobo", 12627315, "mno", "Latn", } m["ate"] = { "Atemble", 4813055, "ngf-wso", "Latn", } m["atg"] = { "Okpela", 7082551, "alv-yek", "Latn", } m["ati"] = { "Attié", 34844, "alv-lag", "Latn", } m["atj"] = { "Atikamekw", 56590, "alg", "Latn", ancestors = "cr", } m["atk"] = { "Ati", 3217458, "phi", "Latn", } m["atl"] = { "Mount Iraya Agta", 6921430, "phi", "Latn", } m["atm"] = { "Ata", 4812603, "phi", "Latn", } m["ato"] = { "Atong (Cameroon)", 34824, "nic-grs", "Latn", } m["atp"] = { "Pudtol Atta", 12640726, "phi", "Latn", } m["atq"] = { "Aralle-Tabulahan", 4783889, "poz-ssw", "Latn", } m["atr"] = { "Waimiri-Atroari", 56865, "sai-car", "Latn", } m["ats"] = { "Gros Ventre", 56628, "alg-ara", "Latn", } m["att"] = { "Pamplona Atta", 12639245, "phi", "Latn", } m["atu"] = { "Reel", 7306882, "sdv-dnu", "Latn", } m["atv"] = { "Northern Altai", 2640863, "trk-ssb", "Cyrl", translit = "Altai-translit", } m["atw"] = { "Atsugewi", 56718, "nai-pal", "Latn", } m["atx"] = { "Arutani", 56609, nil, "Latn", } m["aty"] = { "Aneityum", 2379113, "poz-vns", "Latn", } m["atz"] = { "Arta", 3508067, "phi", "Latn", } m["aua"] = { "Asumboa", 4811870, "poz-tem", "Latn", } m["aub"] = { "Alugu", 12626798, "tbq-urp", "Latn", -- also Hani? } m["auc"] = { "Huaorani", 758570, "qfa-iso", "Latn", } m["aud"] = { "Anuta", 35326, "poz-pnp", "Latn", } m["aug"] = { "Aguna", 34733, "alv-gbe", "Latn", } m["auh"] = { "Aushi", 2872082, "bnt-sbi", "Latn", } m["aui"] = { "Anuki", 3508132, "poz-ocw", "Latn", } m["auj"] = { "Awjila", 56398, "ber", "Latn, Arab, Tfng", } m["auk"] = { "Heyo", 3504295, "paa-hya", "Latn", } m["aul"] = { "Aulua", 427300, "poz-vnc", "Latn", } m["aum"] = { "Asu", 34798, "alv-ngb", "Latn", } m["aun"] = { "Molmo One", 12637224, "paa-trr", "Latn", } m["auo"] = { "Auyokawa", 56247, "cdc-wst", "Latn", } m["aup"] = { "Makayam", 6738863, "paa-tir", "Latn", } m["auq"] = { "Anus", 23855, "poz-ocw", "Latn", } m["aur"] = { "Aruek", 3504279, "paa-kom", "Latn", } m["aut"] = { "Austral", 2669261, "poz-pep", "Latn", } m["auu"] = { "Auye", 4827334, "ngf-pan", "Latn", } m["auw"] = { "Awyi", 3513326, "paa-taa", "Latn", } m["aux"] = { "Aurá", 3507995, "tup-gua", "Latn", } m["auy"] = { "Auyana", 2873211, "ngf-gau", "Latn", } m["auz"] = { "Uzbeki Arabic", 3399507, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["avb"] = { "Avau", 12627412, "poz-ocw", "Latn", } m["avd"] = { "Alviri-Vidari", 3327357, "xme", "Arab", ancestors = "xme-mid", } m["avi"] = { "Avikam", 34840, "alv-lag", "Latn", } m["avk"] = { "Kotava", 1377116, "art", "Latn", type = "appendix-constructed", } m["avm"] = { "Angkamuthi", 62603022, "aus-pmn", "Latn", } m["avn"] = { "Avatime", 34796, "alv-ktg", "Latn", } -- avo Agavotaguerra retired m["avs"] = { "Aushiri", 3409318, "sai-zap", "Latn", } m["avt"] = { "Au", 3446608, "paa-wap", "Latn", } m["avu"] = { "Avokaya", 56685, "csu-mma", "Latn", } m["avv"] = { "Avá-Canoeiro", 4829584, "tup-gua", "Latn", } m["awa"] = { "Awadhi", 29579, "inc-hie", "Deva, Kthi, Aran", translit = { Deva = "hi-translit" }, } m["awb"] = { "Awa (New Guinea)", 2874650, "ngf-gau", "Latn", } m["awc"] = { "Cicipu", 35193, "nic-kam", "Latn", } m["awe"] = { "Awetí", 4830038, "tup", "Latn", } m["awg"] = { "Anguthimri", 4764288, "aus-pam", "Latn", } m["awh"] = { "Awbono", 3446684, "paa-baa", "Latn", } m["awi"] = { "Aekyom", 3399691, "paa-kae", "Latn", } m["awk"] = { "Awabakal", 3449138, "aus-pam", "Latn", } m["awm"] = { "Arawum", 4784537, "ngf-rai", "Latn", } m["awn"] = { "Awngi", 34934, "cus-cen", "Ethi", } m["awo"] = { "Awak", 3446643, "alv-wjk", "Latn", } m["awr"] = { "Awera", 56379, "paa-flp", "Latn", } m["aws"] = { "South Awyu", 12633986, "ngf-awy", "Latn", } m["awt"] = { "Araweté", 4784535, "tup-gua", "Latn", } m["awu"] = { "Central Awyu", 12628801, "ngf-awy", "Latn", } m["awv"] = { "Jair Awyu", 16110177, "ngf-awy", "Latn", } m["aww"] = { "Awun", 56369, "paa-sep", "Latn", } m["awx"] = { "Awara", 2874670, "ngf-waa", "Latn", } m["awy"] = { "Edera Awyu", 12630425, "ngf-awy", "Latn", } m["axb"] = { "Abipón", 11252539, "sai-guc", "Latn", } m["axe"] = { "Ayerrerenge", 16112737, "aus-pam", "Latn", } m["axg"] = { "Arára (Mato Grosso)", 3446660, nil, "Latn", } m["axk"] = { "Aka (Central Africa)", 11010149, "bnt-ngn", "Latn", } m["axl"] = { "Lower Southern Aranda", 6693295, "aus-rnd", "Latn", } m["axm"] = { "Middle Armenian", 4438498, "hyx", "Armn", ancestors = "xcl", -- Armn translit in [[Module:scripts/data]] override_translit = true, strip_diacritics = { remove_diacritics = "՞՜՛՟", from = {"եւ", "ՙ", "՚"}, to = {"և", "ʻ", "’"} } } m["axx"] = { "Xârâgurè", 8045635, "poz-cln", "Latn", } m["aya"] = { "Awar", 56876, "paa-baw", "Latn", } m["ayb"] = { "Ayizo", 34841, "alv-pph", "Latn", } m["ayd"] = { "Ayabadhu", 3509164, "aus-pmn", "Latn", } m["aye"] = { "Ayere", 34788, "alv-aah", "Latn", } m["ayg"] = { "Nyanga (Togo)", 35446, "alv-gng", "Latn", } m["ayi"] = { "Leyigha", 3914492, "nic-uce", "Latn", } m["ayk"] = { "Akuku", 3450179, "alv-nwd", "Latn", } m["ayl"] = { "Libyan Arabic", 56503, "sem-arb", "Arab", strip_diacritics = "ar-stripdiacritics", } m["ayn"] = { "Yemeni Arabic", 1686766, "sem-arb", "Arab, Hebr", strip_diacritics = { Arab = "ar-stripdiacritics", }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["ayo"] = { "Ayoreo", 56634, "sai-zam", "Latn", } m["ayp"] = { "North Mesopotamian Arabic", 56577, "sem-arb", "Arab", ancestors = "acm", strip_diacritics = "ar-stripdiacritics", } m["ayq"] = { "Ayi", 56449, "paa-sep", "Latn", } m["ays"] = { "Sorsogon Ayta", 7563752, "phi", "Latn", } m["ayt"] = { "Bataan Ayta", 4921648, "phi", "Latn", } m["ayu"] = { "Ayu", 34786, "alv", "Latn", } -- ayy deleted and removed from ISO; per the removal request, "no linguistic data exists for any [Ayta] language that the -- ancestors of this group might have once spoken. And thus, there is no evidence that this group ever had a language -- distinct from any other Philippine language." [Lobel] m["ayz"] = { "Maybrat", 4830892, "paa-may", -- either an isolate; grouped with Abun and the West Bird's Head family; or in the putative West Papuan family "Latn", } m["aza"] = { "Azha", 4832486, "tbq-axi", "Latn", } m["azd"] = { "Eastern Durango Nahuatl", 16115449, "azc-dur", "Latn", } m["azg"] = { "San Pedro Amuzgos Amuzgo", 35092, "omq", "Latn", } m["azm"] = { "Ipalapa Amuzgo", 12633013, "omq", "Latn", } m["azn"] = { "Western Durango Nahuatl", 12645553, "azc-dur", "Latn", } m["azo"] = { "Awing", 34856, "nic-nge", "Latn", } m["azt"] = { "Faire Atta", 12630884, "phi", "Latn", } m["azz"] = { "Highland Puebla Nahuatl", 12953754, "azc-nah", "Latn", } return require("Module:languages").finalizeData(m, "language") jtkldq3vzr78tf4m935049xzoxcyu5o Module:links 828 7874 54724 54392 2026-09-25T21:01:42Z Deadend0914 7211 54724 Scribunto text/plain local export = {} --[=[ [[Unsupported titles]], pages with high memory usage, extraction modules and part-of-speech names are listed at [[Module:links/data]]. Other modules used: [[Module:script utilities]] [[Module:scripts]] [[Module:languages]] and its submodules [[Module:gender and number]] [[Module:debug/track]] ]=] local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local form_of_module = "Module:form of" local gender_and_number_module = "Module:gender and number" local languages_module = "Module:languages" local load_module = "Module:load" local memoize_module = "Module:memoize" local pages_module = "Module:pages" local scripts_module = "Module:scripts" local script_utilities_module = "Module:script utilities" local string_encode_entities_module = "Module:string/encode entities" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local find = string.find local get_current_title = mw.title.getCurrentTitle local insert = table.insert local ipairs = ipairs local match = string.match local new_title = mw.title.new local pairs = pairs local remove = table.remove local sub = string.sub local toNFC = mw.ustring.toNFC local tostring = tostring local type = type local unstrip = mw.text.unstrip local NAMESPACE = get_current_title().nsText local function anchor_encode(...) anchor_encode = require(memoize_module)(mw.uri.anchorEncode, true) return anchor_encode(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end -- Can't yet replace, as the [[Module:string utilities]] version no longer has automatic double-encoding prevention, which requires changes here to account for. local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function language_anchor(...) language_anchor = require(anchors_module).language_anchor return language_anchor(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function request_script(...) request_script = require(script_utilities_module).request_script return request_script(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local m_headword_data local function get_headword_data() m_headword_data = load_data("Module:headword/data") return m_headword_data end local function track(page, code) local tracking_page = "links/" .. page debug_track(tracking_page) if code then debug_track(tracking_page .. "/" .. code) end end local function field_non_empty(list, field) if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `termobj.%s`=%s, should be %s\"table\""):format( field, mw.dumpObject(list), field == "q" or field == "qq" and "\"string\" or " or "")) end return list[1] end --[=[ Add any decorations (left or right regular or accent qualifiers, labels or references) to an item. `text` is the item's text (to which to add the decorations) and `itemobj` is the object specifying the item's decorations, which should optionally contain: * left regular qualifiers in `q` (an array of strings or a single string); an empty array will be ignored; * right regular qualifiers in `qq` (an array of strings or a single string); an empty array will be ignored; * left accent qualifiers in `a` (an array of strings); an empty array will be ignored; * right accent qualifiers in `aa` (an array of strings); an empty array will be ignored; * left labels in `l` (an array of strings); an empty array will be ignored; * right labels in `ll` (an array of strings); an empty array will be ignored; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; an empty array will be ignored. `lang` is a language object and is required if any accent qualifiers or labels are given. ]=] local function add_text_decorations(text, itemobj, lang) local q = itemobj.q if type(q) == "string" then q = { q } end local qq = itemobj.qq if type(qq) == "string" then qq = { qq } end if field_non_empty(q, "q") or field_non_empty(qq, "qq") or field_non_empty(itemobj.a, "a") or field_non_empty(itemobj.aa, "aa") or field_non_empty(itemobj.l, "l") or field_non_empty(itemobj.ll, "ll") or field_non_empty(itemobj.refs, "refs") then text = format_decorations { lang = lang, text = text, q = q, qq = qq, a = itemobj.a, aa = itemobj.aa, l = itemobj.l, ll = itemobj.ll, refs = itemobj.refs, } end return text end local function selective_trim(...) -- Unconditionally trimmed charset. local always_trim = "\194\128-\194\159" .. -- U+0080-009F (C1 control characters) "\194\173" .. -- U+00AD (soft hyphen) "\226\128\170-\226\128\174" .. -- U+202A-202E (directionality formatting characters) "\226\129\166-\226\129\169" -- U+2066-2069 (directionality formatting characters) -- Standard trimmed charset. local standard_trim = "%s" .. -- (default whitespace charset) "\226\128\139-\226\128\141" .. -- U+200B-200D (zero-width spaces) always_trim -- If there are non-whitespace characters, trim all characters in `standard_trim`. -- Otherwise, only trim the characters in `always_trim`. selective_trim = function(text) if text == "" then return text end local trimmed = trim(text, standard_trim) if trimmed ~= "" then return trimmed end return trim(text, always_trim) end return selective_trim(...) end local function escape(text, str) local rep repeat text, rep = text:gsub("\\\\(\\*" .. str .. ")", "\5%1") until rep == 0 return (text:gsub("\\" .. str, "\6")) end local function unescape(text, str) return (text :gsub("\5", "\\") :gsub("\6", str)) end -- Remove bold, italics, soft hyphens, strip markers and HTML tags. local function remove_formatting(str) str = str :gsub("('*)'''(.-'*)'''", "%1%2") :gsub("('*)''(.-'*)''", "%1%2") :gsub("­", "") return (unstrip(str) :gsub("<[^<>]+>", "")) end --[==[ Split `text` on double slashes (taking account of escaping backslashes). Return a list of split items. ]==] function export.split_on_slashes(text) if text:find("\\", nil, true) then track("escaped", "split_on_slashes") end text = split(escape(text, "//"), "//", true) or {} for i, v in ipairs(text) do text[i] = unescape(v, "//") if v == "" then text[i] = false end end return text end --[==[ If `text` is a wikilink (i.e. in the form `<nowiki>[[foo]]</nowiki>` or `<nowiki>[[foo|bar]]</nowiki>`), return the link target and display text. If the wikilink is single-part, the display text will be the same as the link target. If the text is not in wikilink form, return {nil} for both link target and display text. ]==] function export.get_wikilink_parts(text) -- TODO: replace `allow_bad_target` with `allow_unsupported`, with support for links to unsupported titles, including escape sequences. if ( -- Filters out anything but "[[...]]" with no intermediate "[[" or "]]". not match(text, "^()%[%[") or -- Faster than sub(text, 1, 2) ~= "[[". find(text, "[[", 3, true) or find(text, "]]", 3, true) ~= #text - 1 ) then return nil, nil end local pipe = find(text, "|", 3, true) local title, display if pipe then title, display = sub(text, 3, pipe - 1), sub(text, pipe + 1, -3) else title = sub(text, 3, -3) display = title end return title, display end -- Does the work of export.get_fragment, but can be called directly to avoid unnecessary checks for embedded links. local function get_fragment(text) text = escape(text, "#") -- Replace numeric character references with the corresponding character (&#39; → '), -- as they contain #, which causes the numeric character reference to be -- misparsed (wa'a → wa&#39;a → pagename wa&, fragment 39;a). text = decode_entities(text) local target, fragment = text:match("^(.-)#(.+)$") target = target or text target = unescape(target, "#") fragment = fragment and unescape(fragment, "#") or nil return target, fragment end --[==[ Given a link target possibly containing a fragment (i.e. a pound sign and following anchor), return two values, the actual target (minus the fragment) and the fragment, or {nil} if there is no fragment. ]==] function export.get_fragment(text) if text:find("\\", nil, true) then track("escaped", "get_fragment") end -- If there are no embedded links, process input. local open = find(text, "[[", nil, true) if not open then return get_fragment(text) end local close = find(text, "]]", open + 2, true) if not close then return get_fragment(text) -- If there is one, but it's redundant (i.e. encloses everything with no pipe), remove and process. elseif open == 1 and close == #text - 1 and not find(text, "|", 3, true) then return get_fragment(sub(text, 3, -3)) end -- Otherwise, return the input. return text, nil end --[==[ Given a link target as passed to `full_link()`, get the actual page that the target refers to. This removes bold, italics, strip markets and HTML; calls `makeEntryName()` for the language in question; converts targets beginning with `*` to the Reconstruction namespace; and converts appendix-constructed languages to the Appendix namespace. Returns up to three values: # the actual page to link to, or {nil} to not link to anything; # how the target should be displayed as, if the user didn't explicitly specify any display text; generally the same as the original target, but minus any anti-asterisk !!; # the value `true` if the target had a backslash-escaped * in it (FIXME: explain this more clearly). FIXME: This should not be calling deprecated `makeEntryName()` and should always return the same number of values. ]==] function export.get_link_page_with_auto_display(target, lang, sc, plain) local orig_target = target if not target then return nil elseif target:find("\\", nil, true) then track("escaped", "get_link_page") end target = remove_formatting(target) if target:sub(1, 1) == ":" then track("initial colon") -- FIXME, the auto_display (second return value) should probably remove the colon return target:sub(2), orig_target end local prefix = target:match("^(.-):") -- Convert any escaped colons target = target:gsub("\\:", ":") if prefix then -- If this is an a link to another namespace or an interwiki link, ensure there's an initial colon and then -- return what we have (so that it works as a conventional link, and doesn't do anything weird like add the term -- to a category.) prefix = ulower(trim(prefix)) if prefix ~= "" and ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then return target, orig_target end end -- Check if the term is reconstructed and remove any asterisk. Also check for anti-asterisk (!!). -- Otherwise, handle the escapes. local reconstructed, escaped, anti_asterisk if not plain then target, reconstructed = target:gsub("^%*(.)", "%1") if reconstructed == 0 then target, anti_asterisk = target:gsub("^!!(.)", "%1") if anti_asterisk == 1 then -- Remove !! from original. FIXME! We do it this way because the call to remove_formatting() above -- may cause non-initial !! to be interpreted as anti-asterisks. We should surely move the -- remove_formatting() call later. orig_target = orig_target:gsub("^!!", "") end end end target, escaped = target:gsub("^(\\-)\\%*", "%1*") if not (sc and sc:getCode() ~= "None") then sc = lang:findBestScript(target) end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). if (not sc:hasCapitalization()) and sc:isTransliterated() and target:match("%^") then target = escape(target, "^") :gsub("%^", "") target = unescape(target, "^") end -- Get the entry name for the language. target = lang:makeEntryName(target, sc, reconstructed == 1 or lang:hasType("appendix-constructed")) -- If the link contains unexpanded template parameters, then don't create a link. if target:match("{{{.-}}}") then -- FIXME: Should we return the original target as the default display value (second return value)? return nil end -- Link to appendix for reconstructed terms and terms in appendix-only languages. Plain links interpret * -- literally, however. if reconstructed == 1 then if lang:getFullCode() == "und" then -- Return the original target as default display value. If we don't do this, we wrongly get -- [Term?] displayed instead. return nil, orig_target end target = "Reconstruction:" .. lang:getFullName() .. "/" .. target -- Reconstructed languages and substrates require an initial *. elseif anti_asterisk ~= 1 and (lang:hasType("reconstructed") or lang:getFamilyCode() == "qfa-sub") then error(("The specified language %s is unattested, while the term '%s' does not begin with '*' to indicate that it is reconstructed.") : format(lang:getCanonicalName(), orig_target)) elseif lang:hasType("appendix-constructed") then target = "Appendix:" .. lang:getFullName() .. "/" .. target else target = target end return target, orig_target, escaped > 0 end function export.get_link_page(target, lang, sc, plain) local target, auto_display, escaped = export.get_link_page_with_auto_display(target, lang, sc, plain) return target, escaped end -- Make a link from a given link's parts. local function make_link(link, lang, sc, id, isolated, cats, no_alt_ast, plain) -- Convert percent encoding to plaintext. link.target = link.target and decode_uri(link.target, "PATH") link.fragment = link.fragment and decode_uri(link.fragment, "PATH") -- Find fragments (if one isn't already set). -- Prevents {{l|en|word#Etymology 2|word}} from linking to [[word#Etymology 2#English]]. -- # can be escaped as \#. if link.target and link.fragment == nil then link.target, link.fragment = get_fragment(link.target) end -- Process the target local auto_display, escaped link.target, auto_display, escaped = export.get_link_page_with_auto_display(link.target, lang, sc, plain) -- Create a default display form. -- If the target is "" then it's a link like [[#English]], which refers to the current page. if auto_display == "" then auto_display = (m_headword_data or get_headword_data()).pagename end -- If the display is the target and the reconstruction * has been escaped, remove the escaping backslash. if escaped then auto_display = auto_display:gsub("\\([^\\]*%*)", "%1", 1) end -- Process the display form. if link.display then local orig_display = link.display link.display = lang:makeDisplayText(link.display, sc, true) if cats then auto_display = lang:makeDisplayText(auto_display, sc) -- If the alt text is the same as what would have been automatically generated, then the alt parameter is -- redundant (e.g. {{l|en|foo|foo}}, {{l|en|w:foo|foo}}, but not {{l|en|w:foo|w:foo}}). If they're -- different, but the alt text could have been entered as the term parameter without it affecting the target -- page, then the target parameter is redundant (e.g. {{l|ru|фу|фу́}}). If `no_alt_ast` is true, use pcall -- to catch the error which will be thrown if this is a reconstructed lang and the alt text doesn't have *. if link.display == auto_display then insert(cats, lang:getFullName() .. " links with redundant alt parameters") else local ok, check if no_alt_ast then ok, check = pcall(export.get_link_page, orig_display, lang, sc, plain) else ok = true check = export.get_link_page(orig_display, lang, sc, plain) end if ok and link.target == check then insert(cats, lang:getFullName() .. " links with redundant target parameters") end end end else link.display = lang:makeDisplayText(auto_display, sc) end if not link.target then return link.display end -- If the target is the same as the current page, there is no sense id -- and either the language code is "und" or the current L2 is the current -- language then return a "self-link" like the software does. if link.target == get_current_title().prefixedText then local fragment, current_L2 = link.fragment, get_current_L2() if ( fragment and fragment == current_L2 or not (id or fragment) and (lang:getFullCode() == "und" or lang:getFullName() == current_L2) ) then return tostring(mw.html.create("strong") :addClass("selflink") :wikitext(link.display)) end end -- Add fragment. Do not add a section link to "Undetermined", as such sections do not exist and are invalid. -- TabbedLanguages handles links without a section by linking to the "last visited" section, but adding -- "Undetermined" would break that feature. For localized prefixes that make syntax error, please use the -- format: ["xyz"] = true. local prefix = link.target:match("^:*([^:]+):") prefix = prefix and ulower(prefix) if prefix ~= "category" and not (prefix and load_data("Module:data/interwikis")[prefix]) then if (link.fragment or link.target:sub(-1) == "#") and not plain then track("fragment", lang:getFullCode()) if cats then insert(cats, lang:getFullName() .. " links with manual fragments") end end if not link.fragment then if id then link.fragment = lang:getFullCode() == "und" and anchor_encode(id) or language_anchor(lang, id) elseif lang:getFullCode() ~= "und" and not (link.target:match("^Appendix:") or link.target:match("^Reconstruction:")) then link.fragment = anchor_encode(lang:getFullName()) end end end -- Put inward-facing square brackets around a link to isolated spacing character(s). if isolated and link.display[1] and not umatch(decode_entities(link.display), "%S") then link.display = "&#x5D;" .. link.display .. "&#x5B;" end link.target = link.target:gsub("^(:?)(.*)", function(m1, m2) return m1 .. encode_entities(m2, "#%&+/:<=>@[\\]_{|}") end) link.fragment = link.fragment and encode_entities(remove_formatting(link.fragment), "#%&+/:<=>@[\\]_{|}") return "[[" .. link.target:gsub("^[^:]", ":%0") .. (link.fragment and "#" .. link.fragment or "") .. "|" .. link.display .. "]]" end -- Split a link into its parts. local function parse_link(linktext) local link = { target = linktext } local target = link.target link.target, link.display = target:match("^(..-)|(.+)$") if not link.target then link.target = target link.display = target end -- There's no point in processing these, as they aren't real links. local target_lower = link.target:lower() for _, false_positive in ipairs({ "category", "cat", "file", "image" }) do if target_lower:match("^" .. false_positive .. ":") then return nil end end link.display = decode_entities(link.display) link.target, link.fragment = get_fragment(link.target) -- So that make_link does not look for a fragment again. if not link.fragment then link.fragment = false end return link end local function check_params_ignored_when_embedded(alt, lang, id, cats) if alt then track("alt-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored alt parameters") end end if id then track("id-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored id parameters") end end end -- Find embedded links and ensure they link to the correct section. local function process_embedded_links(text, alt, lang, sc, id, cats, no_alt_ast, plain) -- Process the non-linked text. text = lang:makeDisplayText(text, sc, true) -- If the text begins with * and another character, then act as if each link begins with *. However, don't do this if the * is contained within a link at the start. E.g. `|*[[foo]]` would set all_reconstructed to true, while `|[[*foo]]` would not. local all_reconstructed = false if not plain then -- anchor_encode removes links etc. if anchor_encode(text):sub(1, 1) == "*" then all_reconstructed = true end -- Otherwise, handle any escapes. text = text:gsub("^(\\-)\\%*", "%1*") end check_params_ignored_when_embedded(alt, lang, id, cats) local function process_link(space1, linktext, space2) local capture = "[[" .. linktext .. "]]" local link = parse_link(linktext) -- Return unprocessed false positives untouched (e.g. categories). if not link then return capture end if all_reconstructed then if link.target:find("^!!") then -- Check for anti-asterisk !! at the beginning of a target, indicating that a reconstructed term -- wants a part of the term to link to a non-reconstructed term, e.g. Old English -- {{ang-noun|m|head=*[[!!Crist|Cristes]] [[!!mæsseǣfen]]}}. link.target = link.target:sub(3) -- Also remove !! from the display, which may have been copied from the target (as in mæsseǣfen in -- the example above). link.display = link.display:gsub("^!!", "") elseif not link.target:match("^%*") then link.target = "*" .. link.target end end linktext = make_link(link, lang, sc, id, false, nil, no_alt_ast, plain) :gsub("^%[%[", "\3") :gsub("%]%]$", "\4") return space1 .. linktext .. space2 end -- Use chars 1 and 2 as temporary substitutions, so that we can use charsets. These are converted to chars 3 and 4 by process_link, which means we can convert any remaining chars 1 and 2 back to square brackets (i.e. those not part of a link). text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- If the script uses ^ to capitalize transliterations, make sure that any carets preceding links are on the inside, so that they get processed with the following text. if ( text:find("^", nil, true) and not sc:hasCapitalization() and sc:isTransliterated() ) then text = escape(text, "^") :gsub("%^\1", "\1%^") text = unescape(text, "^") end text = text:gsub("\1(%s*)([^\1\2]-)(%s*)\2", process_link) -- Remove the extra * at the beginning of a language link if it's immediately followed by a link whose display begins with * too. if all_reconstructed then text = text:gsub("^%*\3([^|\1-\4]+)|%*", "\3%1|*") end return (text :gsub("[\1\3]", "[[") :gsub("[\2\4]", "]]") ) end local function simple_link(term, fragment, alt, lang, sc, id, cats, no_alt_ast, suppress_redundant_wikilink_cat) local plain if lang == nil then lang, plain = get_lang("und"), true end -- Get the link target and display text. If the term is the empty string, treat the input as a link to the current page. if term == "" then term = get_current_title().prefixedText elseif term then local new_term, new_alt = export.get_wikilink_parts(term) if new_term then check_params_ignored_when_embedded(alt, lang, id, cats) -- [[|foo]] links are treated as plaintext "[[|foo]]". -- FIXME: Pipes should be handled via a proper escape sequence, as they can occur in unsupported titles. if new_term == "" then term, alt = nil, term else local title = new_title(new_term) if title then local ns = title.namespace -- File: and Category: links should be returned as-is. if ns == 6 or ns == 14 then return term end end term, alt = new_term, new_alt if cats then if not (suppress_redundant_wikilink_cat and suppress_redundant_wikilink_cat(term, alt)) then insert(cats, lang:getFullName() .. " links with redundant wikilinks") end end end end end if alt then alt = selective_trim(alt) if alt == "" then alt = nil end end -- If there's nothing to process, return nil. if not (term or alt) then return nil end -- If there is no script, get one. if not sc then sc = lang:findBestScript(alt or term) end -- Embedded wikilinks need to be processed individually. if term then local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, alt, lang, sc, id, cats, no_alt_ast, plain) end term = selective_trim(term) end -- If not, make a link using the parameters. return make_link({ target = term, display = alt, fragment = fragment }, lang, sc, id, true, cats, no_alt_ast, plain) end --[==[ Create a basic link to the given term. It links to the language section (such as `==English==`), but it does not add language and script wrappers, so any code that uses this function should call `[[Module:script utilities#tag_text]]` to add such wrappers itself at some point. The first argument, `data`, may contain the following items, a subset of the items used in the `data` argument of `##full_link`. If any other items are included, they are ignored. { { term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", no_alt_ast = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, cats = nil or {}, -- NOTE: If given, will be side-effected to return categories to add the page to. } } Specifically: * `term`: Term to turn into a link. This is generally the name of a page, possibly with extra diacritics added (e.g. length marks in Latin or Old English terms, accents in Russian terms, vowel diacritics in Arabic terms, etc.), which are stripped to determine the actual pagename. The term can contain wikilinks already embedded in it. These are processed individually just like a single link would be. The `alt` argument is ignored in this case. * `alt`: The alternative display for the link, if different from the linked page. If this is {nil}, the `term` argument is used instead (much like regular wikilinks). If `term` contains wikilinks in it, this argument is ignored and has no effect. (Links in which the alt is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/alt-ignored}}.) * `lang` ('''required'''): The [[Module:languages#Language objects|language object]] for the term being linked. The link or links in `term` will normally have their fragment set to point to the canonical name (see {{tl|language data documentation}}) of `lang` (or, if it is an etymology-only language, to the canonical name of its L2 parent). (However, if `id` is defined, the fragment will point to a language-specific sense ID corresponding to this field, which in turn will be overridden by `fragment` if specified.) * `sc`: The [[Module:scripts#Script objects|script object]] for the term being linked. This rarely needs to be specified because it is autodetected based on `term` or `alt`, and the detection is usually correct. It is used to determine how to convert the term into a pagename, possibly by stripping certain diacritics from the term's text. * `fragment`: If specified, overrides the fragment in the generated link (i.e. the portion after `#`, which determines where on the page to go to when the link is clicked). If not specified, the fragment is generated from `id` (if given) or otherwise from `lang`. * `id`: Sense ID string. If this argument is defined, the link will point to a language-specific sense ID ({{ll|en|identifier|id=HTML}}) created by the template {{temp|senseid}}. The fragment for a sense ID consists of the language's canonical name, a hyphen (`-`), and the string that was supplied as the `id` argument. This is useful when a term has more than one sense in a language. If the `term` argument contains wikilinks, this argument is ignored. (Links in which the sense ID is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/id-ignored}}.) * `no_alt_ast`: This is the same as `no_alt_ast` in `##full_link()`. See that function for more information. * `suppress_redundant_wikilink_cat`: This is the same as `suppress_redundant_wikilink_cat` in `##full_link()`. See that function for more information. * `cats`: This should be either {nil} or an empty list. In the latter case, tracking categories will be added to the list when appropriate. The caller can choose to add the page to those categories (as is done by ##full_link()`). The following special options are processed for each link (both simple terms and with embedded wikilinks): * The target page name will be processed by stripping certain diacritics (as mentioned above) and converting the resulting ''logical'' pagename to a ''physical'' pagename (which will be different from the logical pagename in the case of pages with unsupported characters in them and certain overly large pages, such as [[a]], that are split into parts). * If the term starts with `*`, then it is considered a reconstructed term, and a link to the `Reconstruction:` namespace will be created. If the text contains embedded wikilinks, then `*` is automatically applied to each one individually, while preserving the displayed form of each link as it was given. This allows linking to phrases containing multiple reconstructed terms, while only showing the `*` once at the beginning. * If the text starts with `:`, then the link is treated as "raw" and the above steps are skipped. This can be used in rare cases where the page name begins with `*` or if diacritics should not be stripped. For example: ** {{tl|l|en|*nix}} links to the nonexistent page [[Reconstruction:English/nix]] (`*` is interpreted as a reconstruction), but {{tl|l|en|:*nix}} links to [[*nix]]. ** {{tl|l|sl|Franche-Comté}} links to the nonexistent page [[Franche-Comte]] (`é` is converted to `e` by the diacritic-stripping process), but {{tl|l|sl|:Franche-Comté}} links to [[Franche-Comté]]. ]==] function export.language_link(data) if type(data) ~= "table" then error( "The first argument to the function language_link must be a table. See [[Module:links/documentation]] for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "language_link") end -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end return simple_link( data.term, data.fragment, data.alt, lang, data.sc, data.id, cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end function export.plain_link(data) if type(data) ~= "table" then error( "The first argument to the function plain_link must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "plain_link") end return simple_link( data.term, data.fragment, data.alt, nil, data.sc, data.id, data.cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end --[==[Replace any links with links to the correct section, but don't link the whole text if no embedded links are found. Returns the display text form.]==] function export.embedded_language_links(data) if type(data) ~= "table" then error( "The first argument to the function embedded_language_links must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "embedded_language_links") end local term, lang, sc = data.term, data.lang, data.sc -- If we don't have a script, get one. if not sc then sc = lang:findBestScript(term) end -- Do we have embedded wikilinks? If so, they need to be processed individually. local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, data.alt, lang, sc, data.id, data.cats, data.no_alt_ast) end -- If not, return the display text. term = selective_trim(term) -- FIXME: Double-escape any percent-signs, because we don't want to treat non-linked text as having percent-encoded -- characters. This is a hack: percent-decoding should come out of [[Module:languages]] and only dealt with in this -- module, as it's specific to links. term = term:gsub("%%", "%%25") return lang:makeDisplayText(term, sc, true) end function export.mark(text, item_type, face, lang) local tag = { "", "" } if item_type == "gloss" then tag = { '<span class="mention-gloss-double-quote">“</span><span class="mention-gloss">', '</span><span class="mention-gloss-double-quote">”</span>' } if type(text) == "string" and text:match("^''[^'].*''$") then -- Temporary tracking for mention glosses that are entirely italicized or bolded, which is probably -- wrong. (Note that this will also find bolded mention glosses since they use triple apostrophes.) track("italicized-mention-gloss", lang and lang:getFullCode() or nil) end elseif item_type == "tr" then if face == "term" then tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr mention-tr Latn">', '</span>' } else tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr Latn">', '</span>' } end elseif item_type == "ts" then -- \226\129\160 = word joiner (zero-width non-breaking space) U+2060 tag = { '<span class="ts mention-ts Latn">/\226\129\160', '\226\129\160/</span>' } elseif item_type == "pos" then tag = { '<span class="ann-pos">', '</span>' } elseif item_type == "non-gloss" then tag = { '<span class="ann-non-gloss">', '</span>' } elseif item_type == "annotations" then tag = { '<span class="mention-gloss-paren annotation-paren">(</span>', '<span class="mention-gloss-paren annotation-paren">)</span>' } elseif item_type == "infl" then tag = { '<span class="ann-infl">', '</span>' } end if type(text) == "string" then return tag[1] .. text .. tag[2] else return "" end end --[=[ Implementation of `format_transliteration` and `format_transcription`. The implementation is identical except that the field containing the transliteration or transcription may vary and is specified in `field`, and the way a given transliteration or transcription is tagged may vary and is controlled by `tag_fn`, which is passed three parameters: `text` (the transliteration or transcription), `lang` (the language passed in) and `face` (the face passed in). On input, `item` is the transliteration or transcription or list of such objects; `field` is either {"tr"} or {"ts"}; `tag_fn` is a function of three parameters to tag the item, as described above; `lang` is the language object of the term whose transliteration or transcription is specified; and `face` is a string indicating how to display the item (generally only the strings {"term"} and {"default"} are recognized). ]=] local function format_transliteration_or_transcription(item, field, tag_fn, lang, item_face) if type(item) == "string" then return tag_fn(item, lang, item_face) end local formatted_items = {} for _, itemobj in ipairs(item) do local tagged_item = tag_fn(itemobj[field], lang, item_face) tagged_item = add_text_decorations(tagged_item, item, lang) insert(formatted_items, tagged_item) end if formatted_items[2] then -- FIXME: This should be customizable. return concat(formatted_items, " <i>or</i> ") else return formatted_items[1] end end --[==[ Format a transliteration string or list of transliteration objects. `tr` contains the transliteration(s), which for forward compatibility reasons can only be either a single transliteration string or a list of transliteration objects (each of which has a `tr` field holding the transliteration and optional fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transliterations as well as decorations (qualifiers, labels or references) attached to transliterations. ]==] function export.format_transliteration(tr, lang, face) return format_transliteration_or_transcription(tr, "tr", tag_translit, lang, face) end local function tag_transcription(ts, _lang, _face) return export.mark(ts, "ts") end --[==[ Format a transcription string or list of transcription objects. `ts` contains the transcription(s), which for forward compatibility reasons can only be either a single transcription string or a list of transcription objects (each of which has a `ts` field holding the transcription and optional decoration fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transcriptions as well as decorations (qualifiers, labels or references) attached to transcriptions. ]==] function export.format_transcription(ts, lang, face) return format_transliteration_or_transcription(ts, "ts", tag_transcription, lang, face) end local pos_tags --[==[ Format the annotations that are displayed with a link created by `full_link()`. Annotations are the extra bits of information that are displayed following the linked term, and include things such as gender, transliteration, gloss, etc. The first argument is a table with some or all of the following keys (all are optional): * `interwiki`: An interwiki link. This is used for links in translation tables to the corresponding term in another Wiktionary. If specified, it should be a fully formatted link and is inserted as-is at the beginning of the output. See the `interwiki()` function in [[Module:translations]]. * `genders`: Table containing a list of gender specifications in the style of [[Module:gender and number]]. If specified, these are formatted using `format_genders()` in [[Module:gender and number]] and the result inserted at the beginning of the output, following any interwiki link and (in all cases) directly after a no-break space. * `tr`: Transliteration or transliterations. Currently, this is always a one-item list. It is a list because of potential support for per-alternant transliterations due to the multiple alternants (separated by `//`) that can be specified in `term` or `alt`. The item in the list can be either a string or a list of transliteration objects (see `format_transliteration()`). * `ts`: Transcription or transcriptions. Like `tr`, this is currently always a one-item list, with the item being either a single string or a list of transcription objects, as described in `format_transcription()`. * `gloss`: Gloss that translates the term in the link. * `pos`: Part of speech of the linked term. If the given argument matches one of the aliases in `pos_aliases` in [[Module:headword/data]], or consists of a part of speech or alias followed by `f` (for a non-lemma form), expand it appropriately. Otherwise, just show the given text as it is. * `infl`: A list of tags, each a string. Multiple tag sets may be encoded in this list by separating them with an element consisting of a semicolon. If there are multiple tag sets, they are formatted individually and separated by a semicolon + space. * `ng`: Arbitrary non-gloss descriptive text for the link. This should be used in preference to putting descriptive text in `gloss` or `pos`. * `lit`: Literal meaning of the term, if the usual meaning is figurative or idiomatic. * `postprocess_annotations`: A function to postprocess the annotations, after they have been formatted (see below). The `interwiki` and `genders` properties are formatted specially, and `postprocess_annotations` is a callback function rather than an item to display; all others are formatted (each in their own way), separated by commas and placed inside of parentheses (except that if both transliteration and transcription are present, they are separated by a space). The order of the annotations is `tr`+`ts`, `gloss`, `pos`, `infl`, `ng` and `lit`. The `postprocess_annotations` function, if supplied, is passed a single object, a table with two keys `data` (the `data` object passed into `format_link_annotations()`) and `annotations` (the formatted annotations, prior to being concatenated). It should side-effect the `annotations` list, e.g. by inserting more annotations. (It is used to handle nested inflections in [[Module:headword]]. FIXME: It should probably be generalized so that it can return the final formatted string, to allow for e.g. changing the way the annotations are formatted.) * The second argument is a string controlling the "face" that the terms are displayed in. Currently it only affects transliteration and transcription and only when the value {"term"} is passed in, in which case those annotations are displayed italicized. ]==] function export.format_link_annotations(data, face) local output = {} -- Interwiki link if data.interwiki then insert(output, data.interwiki) end -- Genders if type(data.genders) ~= "table" then data.genders = { data.genders } end if data.genders and data.genders[1] then local genders, gender_cats = format_genders(data.genders, data.lang) insert(output, "&nbsp;" .. genders) if gender_cats then local cats = data.cats if cats then extend(cats, gender_cats) end end end local annotations = {} -- Transliteration and transcription local tr = data.tr and data.tr[1] or nil local ts = data.ts and data.ts[1] or nil if tr or ts then local item_face if face == "term" then item_face = face else item_face = "default" end local formatted_tr = tr and export.format_transliteration(tr, data.lang, item_face) or nil local formatted_ts = ts and export.format_transcription(ts, data.lang, item_face) or nil if formatted_tr and formatted_ts then insert(annotations, formatted_tr .. " " .. formatted_ts) else insert(annotations, formatted_tr or formatted_ts) end end -- Gloss/translation if data.gloss then insert(annotations, export.mark(data.gloss, "gloss")) end -- Part of speech if data.pos then -- debug category for pos= containing transcriptions if data.pos:match("/[^><]-/") then data.pos = data.pos .. "[[Category:links likely containing transcriptions in pos]]" end -- Canonicalize part of speech aliases as well as non-lemma aliases like 'nf' or 'nounf' for "noun form". pos_tags = pos_tags or (m_headword_data or get_headword_data()).pos_aliases local pos = pos_tags[data.pos] if not pos and data.pos:find("f$") then local pos_form = data.pos:sub(1, -2) -- We only expand something ending in 'f' if the result is a recognized non-lemma POS. pos_form = (pos_tags[pos_form] or pos_form) .. " form" if (m_headword_data or get_headword_data()).nonlemmas[pos_form .. "s"] then pos = pos_form end end insert(annotations, export.mark(pos or data.pos, "pos")) end -- Inflection data if data.infl then local m_form_of = require(form_of_module) -- Split tag sets manually, since tagged_inflections creates a numbered list, and we do not want that. local infl_outputs = {} local tag_sets = m_form_of.split_tag_set(data.infl) for _, tag_set in ipairs(tag_sets) do table.insert(infl_outputs, m_form_of.tagged_inflections({ tags = tag_set, lang = data.lang, nocat = true, nolink = true, nowrap = true })) end insert(annotations, export.mark(table.concat(infl_outputs, "; "), "infl")) end -- Non-gloss text if data.ng then insert(annotations, export.mark(data.ng, "non-gloss")) end -- Literal/sum-of-parts meaning if data.lit then insert(annotations, "literally " .. export.mark(data.lit, "gloss")) end -- Provide a hook to insert additional annotations such as nested inflections. if data.postprocess_annotations then data.postprocess_annotations { data = data, annotations = annotations } end if annotations[1] then insert(output, " " .. export.mark(concat(annotations, ", "), "annotations")) end return concat(output) end -- Encode certain characters to avoid various delimiter-related issues at various stages. We need to encode < and > -- because they end up forming part of CSS class names inside of <span ...> and will interfere with finding the end -- of the HTML tag. I first tried converting them to URL encoding, i.e. %3C and %3E; they then appear in the URL as -- %253C and %253E, which get mapped back to %3C and %3E when passed to [[Module:accel]]. But mapping them to &lt; -- and &gt; somehow works magically without any further work; they appear in the URL as < and >, and get passed to -- [[Module:accel]] as < and >. I have no idea who along the chain of calls is doing the encoding and decoding. If -- someone knows, please modify this comment appropriately! local accel_char_map local function get_accel_char_map() accel_char_map = { ["%"] = ".", [" "] = "_", ["_"] = u(0xFFF0), ["<"] = "&lt;", [">"] = "&gt;", } return accel_char_map end local function encode_accel_param_chars(param) return (param:gsub("[%% <>_]", accel_char_map or get_accel_char_map())) end local function encode_accel_param(prefix, param) if not param then return "" end if type(param) == "table" then local filled_params = {} -- There may be gaps in the sequence, especially for translit params. local maxindex = 0 for k in pairs(param) do if type(k) == "number" and k > maxindex then maxindex = k end end for i = 1, maxindex do filled_params[i] = param[i] or "" end -- [[Module:accel]] splits these up again. param = concat(filled_params, "*~!") end -- This is decoded again by [[WT:ACCEL]]. return prefix .. encode_accel_param_chars(param) end local function insert_if_not_blank(list, item) if item == "" then return end insert(list, item) end local function get_css_classes(lang, tr, accel, nowrap) if not accel and not nowrap then return "" end local classes = {} if accel then insert(classes, "form-of lang-" .. lang:getFullCode()) local form = accel.form if form then insert(classes, encode_accel_param_chars(form) .. "-form-of") end insert_if_not_blank(classes, encode_accel_param("gender-", accel.gender)) insert_if_not_blank(classes, encode_accel_param("pos-", accel.pos)) insert_if_not_blank(classes, encode_accel_param("transliteration-", accel.translit or (tr ~= "-" and tr or nil))) insert_if_not_blank(classes, encode_accel_param("target-", accel.target)) insert_if_not_blank(classes, encode_accel_param("origin-", accel.lemma)) insert_if_not_blank(classes, encode_accel_param("origin_transliteration-", accel.lemma_translit)) if accel.no_store then insert(classes, "form-of-nostore") end end if nowrap then insert(classes, nowrap) end return concat(classes, " ") end --[==[ Creates a full link, with annotations (see `##format_link_annotations`), in the style of {{tl|l}} or {{tl|m}}. The first argument, `data`, must be a table. It contains the various elements that can be supplied as parameters to {{tl|l}} or {{tl|m}}: { { -- Basic link-related fields term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", accel = {accelerated_creation_tags}, -- Link annotation fields interwiki = "interwiki_link", genders = {"gender1", "gender2", ...} or {{spec = "gender1", q = {"left qualifier", ...}, qq = {"right qualifier"}, ...}, ...}, tr = "transliteration" or "-" or {{tr = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, ts = "transcription" or {{ts = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, gloss = "gloss", pos = "part_of_speech_tag", infl = {"infl1_tag1", "infl1_tag2", ..., ";", "infl2_tag1", "infl2_tag2", ...}, ng = "non-gloss text", lit = "literal_translation", postprocess_annotations = function({data = full_link_data, annotations = {"annotation1", "annotation2", ...}}) -> nil, -- Other transliteration-related fields respect_link_tr = boolean, never_call_transliteration_module = boolean, suppress_tr = boolean, -- Decoration fields q = { "left_qualifier1", "left_qualifier2", ...} or "left_qualifier", qq = { "right_qualifier1", "right_qualifier2", ...} or "right_qualifier", l = { "left_label1", "left_label2", ...}, ll = { "right_label1", "right_label2", ...}, a = { "left_accent_qualifier1", "left_accent_qualifier2", ...}, aa = { "right_accent_qualifier1", "right_accent_qualifier2", ...}, refs = { "formatted_ref1", "formatted_ref2", ...} or { {text = "text", name = "name", group = "group"}, ... }, pretext = "text_at_beginning", posttext = "text_at_end", show_decorations = boolean, -- Fields controlling tracking categories track_sc = boolean, no_nonstandard_sc_cat = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, -- Miscellaneous fields no_alt_ast = boolean, no_generate_alternants = boolean, } } Any one of the items in the `data` table (except for `lang`) may be {nil}. If none of `term`, `alt` and `tr` is present, a term request will be shown. Thus, calling {full_link{ term = term, lang = lang, sc = sc }}, where `term` is the page to link to (which may have diacritics that will be stripped and/or embedded bracketed links) and `lang` is a [[Module:languages#Language objects|language object]] from [[Module:languages]], will give a plain link similar to the one produced by the template {{tl|l}}, and calling {full_link( { term = term, lang = lang, sc = sc }, "term" )} will give a link similar to the one produced by the template {{tl|m}}. The function will: * Try to determine the script, based on the characters found in the `term` or `alt` argument, if the script was not given. If a script is given and `track_sc` is {true}, it will check whether the input script is the same as the one which would have been automatically generated and add the category ` ``lang`` terms with redundant script codes` if yes, or ` ``lang`` terms with non-redundant manual script codes` if no. This should be used when the input script object is directly determined by a template's `sc` parameter. * Call `simple_link()` on the `term` or `alt` forms, to remove diacritics in the page name, process any embedded wikilinks and create links to Reconstruction or Appendix pages when necessary. (`simple_link()` is almost exactly the same as `##language_link()`; the latter is a simple wrapper around the former that adds a bit of extra tracking.) * Call `[[Module:script utilities#tag_text]]` to add the appropriate language and script tags to the term and italicize terms written in the Latin script if necessary. Accelerated creation tags, as used by [[WT:ACCEL]], are included. * Generate a transliteration, based on the `alt` or `term` arguments, if the script is not Latin, no transliteration was provided in `tr` and the combination of the term's language and script support automatic transliteration. The transliteration itself will be linked if both `.respect_link_tr` is specified and the language of the term has the `link_tr` property set for the script of the term; but not otherwise. * Add the annotations (transliteration, gender, gloss, etc.) after the link. * If `no_alt_ast` is specified, then the `alt` text does not need to contain an asterisk if the language is reconstructed. This should only be used by modules which really need to allow links to reconstructions that don't display asterisks (e.g. number boxes). * If `suppress_redundant_wikilink_cat` is specified, it should be a function that indicates whether to suppress the generation of the ` ``lang`` links with redundant wikilinks` tracking category. It is passed two arguments, the `term` and `alt` parameters. Normally, this tracking category is added whenever the `term` argument consists entirely of a one-part or two-part embedded link, which is considered "redundant" in that the link can be rewritten into separate `term` and `alt` arguments without any embedded links. For certain wrapping templates, however, otherwise "redundant" embedded links are necessary to prevent interpretation of certain characters as delimiters. For example, {{tl|col}} and related templates use `~` as a separator, as well as `,` when not followed by a space. In these templates, embedded links are required to correctly link to terms containing those delimiters, such as [[Micros~1]] and [[1,6-Cleves acid]], but will incorrectly trigger the addition of the tracking category unless the appropriate `suppress_redundant_wikilink_cat` function is given. * If `pretext` or `posttext` is specified, this is text to (respectively) prepend or append to the output, directly before processing decorations (qualifiers, labels and references). This can be used to add arbitrary extra text inside of the decorations. * If `show_decorations` is specified, then decorations specified in `data` (i.e. left and right qualifiers, accent qualifiers, labels and references) will be displayed, otherwise they will be ignored. (This is because a fair amount of code stores decorations in these fields and displays them itself, rather than expecting {full_link()} to display them.) * ]==] function export.full_link(data, face, allow_self_link, show_qualifiers) if type(data) ~= "table" then error("The first argument to the function full_link must be a table. " .. "See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "full_link") end if show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-17, remove after 2026-10-17 or so. error("Can't pass fourth parameter `show_qualifiers` any more. Set `show_decorations = true` on data.") end if data.show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-18, remove after 2026-10-18 or so. error("Can't set field `show_qualifiers`; use `show_decorations`") end if data.no_generate_forms then -- FIXME: Eventually remove the error code. Added 2026-09-14, remove after 2026-10-14 or so. error("Can't set field `no_generate_forms`; use `no_generate_alternants`") end -- Prevent data from being destructively modified. data = shallow_copy(data) data.cats = {} -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end local terms = { true } -- Generate multiple alternants if applicable. for _, param in ipairs { "term", "alt" } do if type(data[param]) == "string" and data[param]:find("//", nil, true) then data[param] = export.split_on_slashes(data[param]) elseif type(data[param]) == "string" and not (type(data.term) == "string" and data.term:find("//", nil, true)) then if not data.no_generate_alternants then data[param] = lang:generateAlternants(data[param]) else data[param] = { data[param] } end else data[param] = {} end end for _, param in ipairs { "sc", "tr", "ts" } do data[param] = { data[param] } end for _, param in ipairs { "term", "alt", "sc", "tr", "ts" } do for i in pairs(data[param]) do terms[i] = true end end -- Create the link local outparts = {} local id, no_alt_ast, suppress_redundant_wikilink_cat, accel, never_call_transliteration_module = data.id, data.no_alt_ast, data.suppress_redundant_wikilink_cat, data.accel, data.never_call_transliteration_module local link_tr = data.respect_link_tr and lang:link_tr(data.sc[1]) for i in ipairs(terms) do local link -- Is there any text to show? if (data.term[i] or data.alt[i]) then -- Try to detect the script if it was not provided local display_term = data.alt[i] or data.term[i] local best = lang:findBestScript(display_term) -- no_nonstandard_sc_cat is intended for use in [[Module:interproject]] if ( not data.no_nonstandard_sc_cat and best:getCode() == "None" and find_best_script_without_lang(display_term):getCode() ~= "None" ) then insert(cats, lang:getFullName() .. " terms in nonstandard scripts") end if not data.sc[i] then data.sc[i] = best -- Track uses of sc parameter. elseif data.track_sc then if data.sc[i]:getCode() == best:getCode() then insert(cats, lang:getFullName() .. " terms with redundant script codes") else insert(cats, lang:getFullName() .. " terms with non-redundant manual script codes") end end -- If using a discouraged character sequence, add to maintenance category if data.sc[i]:hasNormalizationFixes() == true then if (data.term[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.term[i])) ~= toNFC(data.term[i])) or (data.alt[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.alt[i])) ~= toNFC(data.alt[i])) then insert(cats, "Pages using discouraged character sequences") end end link = simple_link( data.term[i], data.fragment, data.alt[i], lang, data.sc[i], id, cats, no_alt_ast, suppress_redundant_wikilink_cat ) end -- simple_link can return nil, so check if a link has been generated. if link then -- Add "nowrap" class to prefixes in order to prevent wrapping after the hyphen local nowrap local display_term = data.alt[i] or data.term[i] if display_term and (display_term:find("^%-") or display_term:find("^־")) then -- Hebrew maqqef -- FIXME, use hyphens from [[Module:affix]] nowrap = "nowrap" end link = tag_text(link, lang, data.sc[i], face, get_css_classes(lang, data.tr[i], accel, nowrap)) else --[[ No term to show. Is there at least a transliteration we can work from? ]] link = request_script(lang, data.sc[i]) -- No link to show, and no transliteration either. Show a term request (unless it's a substrate, as they rarely take terms). if (link == "" or (not data.tr[i]) or data.tr[i] == "-") and lang:getFamilyCode() ~= "qfa-sub" then -- If there are multiple terms, break the loop instead. if i > 1 then remove(outparts) break elseif NAMESPACE ~= "Template" then insert(cats, lang:getFullName() .. " term requests") end link = "<small>[Term?]</small>" end end insert(outparts, link) if i < #terms then insert(outparts, "<span class=\"Zsym mention\" style=\"font-size:100%;\">&nbsp;/ </span>") end end -- When suppress_tr is true, do not show or generate any transliteration if data.suppress_tr then data.tr[1] = nil else -- TODO: Currently only handles the first transliteration, pending consensus on how to handle multiple translits for multiple forms, as this is not always desirable (e.g. traditional/simplified Chinese). if data.tr[1] == "" or data.tr[1] == "-" then data.tr[1] = nil else local phonetic_extraction = load_data("Module:links/data").phonetic_extraction phonetic_extraction = phonetic_extraction[lang:getCode()] or phonetic_extraction[lang:getFullCode()] if phonetic_extraction then data.tr[1] = data.tr[1] or require(phonetic_extraction).getTranslit(export.remove_links(data.alt[1] or data.term[1])) elseif (data.term[1] or data.alt[1]) and data.sc[1]:isTransliterated() then -- Track whenever there is manual translit. The categories below like 'terms with redundant transliterations' -- aren't sufficient because they only work with reference to automatic translit and won't operate at all in -- languages without any automatic translit, like Persian and Hebrew. if data.tr[1] then local full_code = lang:getFullCode() track("manual-tr", full_code) end if not never_call_transliteration_module then -- Try to generate a transliteration. local text = data.alt[1] or data.term[1] if not link_tr then text = export.remove_links(text, true) end local automated_tr = lang:transliterate(text, data.sc[1]) if automated_tr then local manual_tr = data.tr[1] if manual_tr then if export.remove_links(manual_tr) == export.remove_links(automated_tr) then insert(cats, lang:getFullName() .. " terms with redundant transliterations") else -- Prevents Arabic root categories from flooding the tracking categories. if NAMESPACE ~= "Category" then insert(cats, lang:getFullName() .. " terms with non-redundant manual transliterations") end end end if not manual_tr or lang:overrideManualTranslit(data.sc[1]) then data.tr[1] = automated_tr end end end end end end -- Link to the transliteration entry for languages that require this if data.tr[1] and link_tr and not data.tr[1]:match("%[%[(.-)%]%]") then data.tr[1] = simple_link( data.tr[1], nil, nil, lang, get_script("Latn"), nil, cats, no_alt_ast, suppress_redundant_wikilink_cat ) elseif data.tr[1] and not link_tr then -- Remove the pseudo-HTML tags added by remove_links. data.tr[1] = data.tr[1]:gsub("</?link>", "") end if data.tr[1] and not umatch(data.tr[1], "[^%s%p]") then data.tr[1] = nil end insert(outparts, export.format_link_annotations(data, face)) if data.pretext then insert(outparts, 1, data.pretext) end if data.posttext then insert(outparts, data.posttext) end local categories = cats[1] and format_categories(cats, lang, "-", nil, nil, data.sc) or "" local output = concat(outparts) if data.show_decorations then output = add_text_decorations(output, data, lang) end return output .. categories end --[==[ Strip links by replacing all wikilinks with their displayed text, and remove any categories. This function can be invoked either from a template or from another module. Specifically, this function deletes category links, the targets of piped links, and any double square brackets involved in links (other than file links, which are untouched). If `tag` is set, then any links removed will be given pseudo-HTML tags, which allow the substitution functions in [[Module:languages]] to properly subdivide the text in order to reduce the chance of substitution failures in modules which scrape pages like [[Module:zh-translit]]. (FIXME: This is quite hacky. We probably want this to be integrated into [[Module:languages]], but we can't do that until we know that nothing is pushing pipe linked transliterations through it for languages which don't have link_tr set.) * `<nowiki>[[page|displayed text]]</nowiki>` &rarr; `displayed text` * `<nowiki>[[page and displayed text]]</nowiki>` &rarr; `page and displayed text` * `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` &rarr; ''(nothing)'' ]==] function export.remove_links(text, tag) if type(text) == "table" then text = text.args[1] end if not text or text == "" then return "" end text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- Parse internal links for the display text. text = text:gsub("(\1)([^\1\2]-)(\2)", function(c1, c2, c3) -- Don't remove files. for _, false_positive in ipairs({ "file", "image" }) do if c2:lower():match("^" .. false_positive .. ":") then return c1 .. c2 .. c3 end end -- Remove categories completely. for _, false_positive in ipairs({ "category", "cat" }) do if c2:lower():match("^" .. false_positive .. ":") then return "" end end -- In piped links, remove all text before the pipe, unless it's the final character (i.e. the pipe trick), in which case just remove the pipe. c2 = c2:match("^[^|]*|(.+)") or c2:match("([^|]+)|$") or c2 if tag then return "<link>" .. c2 .. "</link>" else return c2 end end) text = text :gsub("\1", "[[") :gsub("\2", "]]") return text end function export.section_link(link) if type(link) ~= "string" then error("The first argument to section_link was a " .. type(link) .. ", but it should be a string.") elseif link:find("\\", nil, true) then track("escaped", "section_link") end local target, section = get_fragment((link:gsub("_", " "))) if not section then error("No \"#\" delineating a section name") end return simple_link( target, section, target .. " §&nbsp;" .. section ) end return export ttb99v40btmeqphqdixgae95woinbni Module:links/data 828 7877 54725 54395 2026-09-25T21:03:16Z Deadend0914 7211 54725 Scribunto text/plain local data = {} local unpack = unpack or table.unpack -- Lua 5.2 compatibility local u = require("Module:string utilities").char data.phonetic_extraction = { ["th"] = "Module:th", ["km"] = "Module:km", } data.ignored_prefixes = { ["cat"] = true, ["category"] = true, ["file"] = true, ["image"] = true } -- Scheme for using unsupported characters in titles. data.unsupported_characters = { ["#"] = "`num`", ["%"] = "`percnt`", -- only escaped in percent encoding ["&"] = "`amp`", -- only escaped in HTML entities ["."] = "`period`", -- only escaped in dot-slash notation ["<"] = "`lt`", [">"] = "`gt`", ["["] = "`lsqb`", ["]"] = "`rsqb`", ["_"] = "`lowbar`", ["`"] = "`grave`", -- used to enclose unsupported characters in the scheme, so a raw use in an unsupported title must be escaped to prevent interference ["{"] = "`lcub`", ["|"] = "`vert`", ["}"] = "`rcub`", ["~"] = "`tilde`", -- only escaped when 3 or more are consecutive ["\239\191\189"] = "`repl`" -- replacement character U+FFFD, which can't be typed directly here due to an abuse filter } -- Manually specified unsupported titles. Only put titles here if there is a different reason why they are unsupported, and not just because they contain one of the unsupported characters above. data.unsupported_titles = { [" "] = "Space", ["&amp;"] = "`amp`amp;", ["λοπαδοτεμαχοσελαχογαλεοκρανιολειψανοδριμυποτριμματοσιλφιοκαραβομελιτοκατακεχυμενοκιχλεπικοσσυφοφαττοπεριστεραλεκτρυονοπτοκεφαλλιοκιγκλοπελειολαγῳοσιραιοβαφητραγανοπτερύγων"] = "Ancient Greek dish", ["กรุงเทพมหานคร อมรรัตนโกสินทร์ มหินทรายุธยา มหาดิลกภพ นพรัตนราชธานีบูรีรมย์ อุดมราชนิเวศน์มหาสถาน อมรพิมานอวตารสถิต สักกะทัตติยวิษณุกรรมประสิทธิ์"] = "Thai name of Bangkok", [u(0x1680)] = "Ogham space", [u(0x3000)] = "Ideographic space" } -- Mammoth pages contain only Translingual and English entries, if present. The remaining L2s are placed on subpages. -- The same subpage titles are used across all mammoth pages for the convenience of bot and script operators. -- Assuming that most mammoth pages will be Latin-script terms, the subpage groupings are determined by dividing the -- list of Latin-script languages known to Wiktionary into two (three, ...) roughly equal alphabetic divisions. This is -- easily done by looking at Petscan's output: -- https://petscan.wmcloud.org/?sortby=title&language=en&ns%5B14%5D=1&categories=Latin+script+languages&project=wiktionary&doit= -- This data structure contains types of splits, each of which is a list of names of splits and Lua patterns applied to -- the decomposed L2 name (with apostrophes and double quotes removed and certain other transformations applied; see -- get_L2_sort_key() in [[Module:headword/page]]), or "true" for the final catch-all subpage (which includes anything -- not beginning with a Latin letter after the transformations are applied; this includes e.g. ǃKung but not 'Are'are, -- which sorts with A, and not Àhàn, which likewise sorts with A). The patterns must be suitable for use with plain -- string functions, not their mw.ustring equivalents. data.mammoth_page_subpage_types = { twos = { {"languages A to L", "^[A-L]"}, {"languages M to Z", true}, }, threes = { {"languages A to I", "^[A-I]"}, {"languages J to Q", "^[J-Q]"}, {"languages R to Z", true}, }, CJK = { {"languages A to C", "^[A-C]"}, -- Translingual and Chinese on one page {"languages D to Z", true}, -- all the remainder (mostly Japanese, Korean, Vietnamese) on the other }, } -- "Mammoth pages" are pages whose entries cannot be housed on a single page because of MediaWiki limits. The key is -- the page and the value is the subpage type, as defined above in `mammoth_page_subpage_types`. data.mammoth_pages = { ["a"] = "twos", -- FIXME: change to threes ["i"] = "twos", ["mammoth page test"] = "twos", -- required for testing purposes - please leave here } return data 9n45na7udwk645td2vxc6cwry17hnx5 glēaw 0 7938 54682 54640 2026-09-25T16:30:26Z Deadend0914 7211 /* Englisc */ 54682 wikitext text/x-wiki ==Englisc== ===Wordstǣr=== Of Ealdorlic Westernegermanisc ''*glauw'' ===Rihtstefn=== IPA: /ɡlæ͜ɑːw/ ===Tōgeīecendlice=== '''glēaw''' # Sċēawiende gōdne dóm. ===Declīnung=== {{ang-adecl|glēaw}} ===Wendunga=== * Nīwenglisc: [[wise]] rd8g1kkfhalxfjcy6sn885j71joq66a Module:minitoc 828 7948 54671 2026-09-25T15:51:37Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local headword_data_module = "Module:headword/data" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded f...' 54671 Scribunto text/plain local export = {} local headword_data_module = "Module:headword/data" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local L2_list local function get_L2_list() L2_list, get_L2_list = mw.loadData(headword_data_module).page.L2_list, nil return L2_list end function export.main(frame) process_params(frame:getParent().args, {}) local L2s_len, main, pre = (L2_list or get_L2_list()).n for i = 1, L2s_len do local L2, t = L2_list[i] if L2 == "Translingual" or L2 == "English" then pre = pre or {} t = pre else main = main or {} t = main end insert(t, "[[#" .. L2 .. "|" .. L2 .. "]]") end return frame:extensionTag("templatestyles", nil, {src="Module:minitoc/styles.css"}) .. "<div class=\"minitoc NavFrame\" data-toggle-category=\"table of contents\" data-minitoc-length=\"" .. L2s_len .. "\"><div class=\"NavHead\">Languages (" .. L2s_len .. ")</div><div class=\"NavContent\">" .. (pre and concat(pre, " •&nbsp;") .. (main and "<hr>" or "") or "") .. (main and concat(main, " •&nbsp;") or "") .. "<hr>[[#catlinks|Page categories]]</div></div><div data-toc-length=\"" .. L2s_len .. "\">__TOC__</div>" .. format_categories("Pages calling Template:minitoc", nil, nil, nil, true) end return export q8idqjmyinr9qskdn43stwkftumum7t Module:minitoc/styles.css 828 7949 54672 2026-09-25T15:53:35Z Deadend0914 7211 Gesceop tramet þe hafaþ '.minitoc { max-width: 40em; margin: 0.5em 0; } .minitoc .NavHead { padding: 0.25em 0.5em; } .minitoc .NavContent { padding: 0.5em; } .minitoc a { white-space: nowrap; } /* Default behaviour: hide miniTOC if 1-19 L2s and hide TOC if 20+ L2s. */ [data-minitoc-length="1"], [data-minitoc-length="2"], [data-minitoc-length="3"], [data-minitoc-length="4"], [data-minitoc-length="5"], [data-minitoc-length="6"], [data-minitoc-length="7"], [data-minitoc-length="...' 54672 sanitized-css text/css .minitoc { max-width: 40em; margin: 0.5em 0; } .minitoc .NavHead { padding: 0.25em 0.5em; } .minitoc .NavContent { padding: 0.5em; } .minitoc a { white-space: nowrap; } /* Default behaviour: hide miniTOC if 1-19 L2s and hide TOC if 20+ L2s. */ [data-minitoc-length="1"], [data-minitoc-length="2"], [data-minitoc-length="3"], [data-minitoc-length="4"], [data-minitoc-length="5"], [data-minitoc-length="6"], [data-minitoc-length="7"], [data-minitoc-length="8"], [data-minitoc-length="9"], [data-minitoc-length="10"], [data-minitoc-length="11"], [data-minitoc-length="12"], [data-minitoc-length="13"], [data-minitoc-length="14"], [data-minitoc-length="15"], [data-minitoc-length="16"], [data-minitoc-length="17"], [data-minitoc-length="18"], [data-minitoc-length="19"] { display: none; } [data-toc-length]:not([data-toc-length="1"]):not([data-toc-length="2"]):not([data-toc-length="3"]):not([data-toc-length="4"]):not([data-toc-length="5"]):not([data-toc-length="6"]):not([data-toc-length="7"]):not([data-toc-length="8"]):not([data-toc-length="9"]):not([data-toc-length="10"]):not([data-toc-length="11"]):not([data-toc-length="12"]):not([data-toc-length="13"]):not([data-toc-length="14"]):not([data-toc-length="15"]):not([data-toc-length="16"]):not([data-toc-length="17"]):not([data-toc-length="18"]):not([data-toc-length="19"]) { display: none; } /* In Vector 2022: hide miniTOC if there are 1-9 L2s and always show TOC. */ /* This overrides the default miniTOC style. It is not necessary to override the default TOC style as it already gets invalidated. */ body.skin-vector-2022 [data-minitoc-length="10"], body.skin-vector-2022 [data-minitoc-length="11"], body.skin-vector-2022 [data-minitoc-length="12"], body.skin-vector-2022 [data-minitoc-length="13"], body.skin-vector-2022 [data-minitoc-length="14"], body.skin-vector-2022 [data-minitoc-length="15"], body.skin-vector-2022 [data-minitoc-length="16"], body.skin-vector-2022 [data-minitoc-length="17"], body.skin-vector-2022 [data-minitoc-length="18"], body.skin-vector-2022 [data-minitoc-length="19"] { display: block; } /* On mobile with width < 720px: hide miniTOC if there are 1-4 L2s, and hide TOC if 5+ L2s. */ /* This overrides the default miniTOC and TOC styles. */ @media screen and (max-width: 719px) { body.skin-minerva [data-minitoc-length="5"], body.skin-minerva [data-minitoc-length="6"], body.skin-minerva [data-minitoc-length="7"], body.skin-minerva [data-minitoc-length="8"], body.skin-minerva [data-minitoc-length="9"], body.skin-minerva [data-minitoc-length="10"], body.skin-minerva [data-minitoc-length="11"], body.skin-minerva [data-minitoc-length="12"], body.skin-minerva [data-minitoc-length="13"], body.skin-minerva [data-minitoc-length="14"], body.skin-minerva [data-minitoc-length="15"], body.skin-minerva [data-minitoc-length="16"], body.skin-minerva [data-minitoc-length="17"], body.skin-minerva [data-minitoc-length="18"], body.skin-minerva [data-minitoc-length="19"] { display: block; } body.skin-minerva [data-toc-length]:not([data-toc-length="1"]):not([data-toc-length="2"]):not([data-toc-length="3"]):not([data-toc-length="4"]) { display: block; } } c8g6qy3ftcfwosz00bdf6071oj24qgq Bysen:minitoc 10 7950 54673 2026-09-25T15:57:19Z Deadend0914 7211 Gesceop tramet þe hafaþ '<includeonly>{{#invoke:minitoc|main}}</includeonly>' 54673 wikitext text/x-wiki <includeonly>{{#invoke:minitoc|main}}</includeonly> sz4jir3406yg77m5tasnappaau95rlq Module:ang-adjective 828 7951 54675 2026-09-25T16:15:27Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[=[ This module contains functions for creating inflection tables for Old English adjectives. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") lo...' 54675 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Old English adjectives. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Masculine]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Feminine]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Neuter]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nominative]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Accusative]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Genitive]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Dative]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Instrumental]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Masculine]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Feminine]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Neuter]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nominative]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Accusative]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Genitive]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Dative]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Instrumental]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declension of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Singular]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Plural]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Singular]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Plural]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strong") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Weak") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export e4m70vuox9vnxmxmks7riaqyzhh40ka 54677 54675 2026-09-25T16:22:55Z Deadend0914 7211 54677 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Old English adjectives. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declension of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Singular]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Plural]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Singular]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Plural]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strong") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Weak") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export f2x4eatov29isgu5uxmx4merhla15b2 54678 54677 2026-09-25T16:25:26Z Deadend0914 7211 54678 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Old English adjectives. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declension of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strang") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Unstrang") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export h3nuu4lblohe3rv0algq8m0pt3pbfzk 54679 54678 2026-09-25T16:26:24Z Deadend0914 7211 54679 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Old English adjectives. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declīnung of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strang") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Unstrang") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export 0cqgttm8eyee5vzfb5zmalb4vngo92f 54791 54679 2026-09-26T01:50:45Z Deadend0914 7211 54791 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Englisc tōgeīecendlic. It implements {{ang-adecl}}. Author: Benwing2 External entry points: show(): For {{ang-adecl}} make_table(): For {{ang-decl-adj-table}} (no longer in existence) ]=]-- local m_links = require("Module:links") local strutils = require("Module:string utilities") local m_table = require("Module:table") local lang = require("Module:languages").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declīnung of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strang") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Unstrang") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export q9e01cvcepdolgx508mcklybkze2fa1 Bysen:ang-adecl 10 7952 54676 2026-09-25T16:16:14Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:ang-adjective|show}}<!--' 54676 wikitext text/x-wiki {{#invoke:ang-adjective|show}}<!-- bb7fgjovs58xiy7oadpfd39umhdrpmb Module:ang-noun 828 7953 54683 2026-09-25T16:34:19Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local m_links = require("Module:links") local strutils = require("Module:string utilities") local lang = require("Module:languages").getByCode("ang") local export = {} local m_inflection_table = require("Module:inflection-table") local cases = { "nom", "acc", "gen", "dat" } local numbers = { "sg", "pl" } local slots_to_accel_form = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do slots_to_accel_form[case .. "_" .. number] = case .. "|"...' 54683 Scribunto text/plain local m_links = require("Module:links") local strutils = require("Module:string utilities") local lang = require("Module:languages").getByCode("ang") local export = {} local m_inflection_table = require("Module:inflection-table") local cases = { "nom", "acc", "gen", "dat" } local numbers = { "sg", "pl" } local slots_to_accel_form = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do slots_to_accel_form[case .. "_" .. number] = case .. "|" .. number:sub(1, 1) end end local slots_to_args = { nom_sg = 1, nom_pl = 2, acc_sg = 3, acc_pl = 4, gen_sg = 5, gen_pl = 6, dat_sg = 7, dat_pl = 8, } function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = true, -- To be removed. ["type"] = true, ["width"] = true, ["style"] = true, ["num"] = true, ["g"] = true, ["gender"] = {alias_of = "g"}, } for slot, arg in pairs(slots_to_args) do params[arg] = {list = slot} end local args = require("Module:parameters").process(parent_args, params) local table_args = { style = args.style == "right" and "float:right; clear:right;" or "", width = args.width or "30", } local accel_lemma_sg = args[slots_to_args.nom_sg][1] local accel_lemma_pl = args[slots_to_args.nom_pl][1] local accel_lemma = accel_lemma_sg and accel_lemma_sg ~= "—" and accel_lemma_sg ~= "-" and accel_lemma_sg:gsub(",.*", "") or accel_lemma_pl and accel_lemma_pl ~= "—" and accel_lemma_pl ~= "-" and accel_lemma_pl:gsub(",.*", "") or nil for slot, accel_form in pairs(slots_to_accel_form) do local form_args = args[slots_to_args[slot]] if #form_args == 0 then form_args = {"—"} end local forms = {} for _, form_arg in ipairs(form_args) do for _, form in ipairs(mw.text.split(form_arg, ", *")) do table.insert(forms, form) end end local table_arg = {} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end if args.num == "pl" then table_args.nom_sg = "—" table_args.acc_sg = "—" table_args.gen_sg = "—" table_args.dat_sg = "—" elseif args.num == "sg" then table_args.nom_pl = "—" table_args.acc_pl = "—" table_args.gen_pl = "—" table_args.dat_pl = "—" end local type = '' if args["type"] then type = type .. (args["type"]:gsub("^%l", string.upper)) end if args.g then type = type .. " (" .. ( args.g == "m" and "masculine" or args.g == "f" and "feminine" or args.g == "n" and "neuter" or error("Unrecognized gender '" .. args.g .. "'") ) .. ")" end type = type .. ':' local table = type .. (type and '\n') table = table .. m_inflection_table.make_top{ title = '-', palette = 'indigo' } table = table .. [=[ ! ! singular ! plural |- ! [[nominative case|nominative]] | data-accel-col=1 | {nom_sg} | data-accel-col=2 | {nom_pl} |- ! [[accusative case|accusative]] | data-accel-col=1 | {acc_sg} | data-accel-col=2 | {acc_pl} |- ! [[genitive case|genitive]] | data-accel-col=1 | {gen_sg} | data-accel-col=2 | {gen_pl} |- ! [[dative case|dative]] | data-accel-col=1 | {dat_sg} | data-accel-col=2 | {dat_pl} ]=] table = table .. m_inflection_table.make_bottom{} return strutils.format(table, table_args) end return export -- For Vim, so we get 4-space tabs -- vim: set ts=4 sw=4 noet: 5tjcsumrrkpdksgucxan0t27sgrkcis 54684 54683 2026-09-25T16:38:10Z Deadend0914 7211 54684 Scribunto text/plain local m_links = require("Module:links") local strutils = require("Module:string utilities") local lang = require("Module:languages").getByCode("ang") local export = {} local m_inflection_table = require("Module:inflection-table") local cases = { "nom", "acc", "gen", "dat" } local numbers = { "sg", "pl" } local slots_to_accel_form = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do slots_to_accel_form[case .. "_" .. number] = case .. "|" .. number:sub(1, 1) end end local slots_to_args = { nom_sg = 1, nom_pl = 2, acc_sg = 3, acc_pl = 4, gen_sg = 5, gen_pl = 6, dat_sg = 7, dat_pl = 8, } function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = true, -- To be removed. ["type"] = true, ["width"] = true, ["style"] = true, ["num"] = true, ["g"] = true, ["gender"] = {alias_of = "g"}, } for slot, arg in pairs(slots_to_args) do params[arg] = {list = slot} end local args = require("Module:parameters").process(parent_args, params) local table_args = { style = args.style == "right" and "float:right; clear:right;" or "", width = args.width or "30", } local accel_lemma_sg = args[slots_to_args.nom_sg][1] local accel_lemma_pl = args[slots_to_args.nom_pl][1] local accel_lemma = accel_lemma_sg and accel_lemma_sg ~= "—" and accel_lemma_sg ~= "-" and accel_lemma_sg:gsub(",.*", "") or accel_lemma_pl and accel_lemma_pl ~= "—" and accel_lemma_pl ~= "-" and accel_lemma_pl:gsub(",.*", "") or nil for slot, accel_form in pairs(slots_to_accel_form) do local form_args = args[slots_to_args[slot]] if #form_args == 0 then form_args = {"—"} end local forms = {} for _, form_arg in ipairs(form_args) do for _, form in ipairs(mw.text.split(form_arg, ", *")) do table.insert(forms, form) end end local table_arg = {} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end if args.num == "pl" then table_args.nom_sg = "—" table_args.acc_sg = "—" table_args.gen_sg = "—" table_args.dat_sg = "—" elseif args.num == "sg" then table_args.nom_pl = "—" table_args.acc_pl = "—" table_args.gen_pl = "—" table_args.dat_pl = "—" end local type = '' if args["type"] then type = type .. (args["type"]:gsub("^%l", string.upper)) end if args.g then type = type .. " (" .. ( args.g == "m" and "masculine" or args.g == "f" and "feminine" or args.g == "n" and "neuter" or error("Unrecognized gender '" .. args.g .. "'") ) .. ")" end type = type .. ':' local table = type .. (type and '\n') table = table .. m_inflection_table.make_top{ title = '-', palette = 'indigo' } table = table .. [=[ ! ! ānfeald ! manigfeald |- ! [[nominative case|nemniendlic]] | data-accel-col=1 | {nom_sg} | data-accel-col=2 | {nom_pl} |- ! [[accusative case|wrēgendlic]] | data-accel-col=1 | {acc_sg} | data-accel-col=2 | {acc_pl} |- ! [[genitive case|āgniendlic]] | data-accel-col=1 | {gen_sg} | data-accel-col=2 | {gen_pl} |- ! [[dative case|forgifendlic]] | data-accel-col=1 | {dat_sg} | data-accel-col=2 | {dat_pl} ]=] table = table .. m_inflection_table.make_bottom{} return strutils.format(table, table_args) end return export -- For Vim, so we get 4-space tabs -- vim: set ts=4 sw=4 noet: pdyyho7015nd7evuym19dy02jgq7voo Bysen:ang-decl-noun-a-m 10 7954 54685 2026-09-25T16:40:16Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strong ''a''-stem<!-- -->|1={{{nomsg|{{{1}}}}}}<!-- -->|3={{{nomsg|{{{1}}}}}}<!-- -->|5={{{1}}}{{#if:{{{vowel|}}}||e}}s<!-- -->|7={{{datsg|{{{1}}}{{#if:{{{vowel|}}}||e}}}}}<!-- -->|2={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||a}}s<!-- -->|4={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||a}}s<!-- -->|6={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}|n}}a<!-- -->|8={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||u}}...' 54685 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strong ''a''-stem<!-- -->|1={{{nomsg|{{{1}}}}}}<!-- -->|3={{{nomsg|{{{1}}}}}}<!-- -->|5={{{1}}}{{#if:{{{vowel|}}}||e}}s<!-- -->|7={{{datsg|{{{1}}}{{#if:{{{vowel|}}}||e}}}}}<!-- -->|2={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||a}}s<!-- -->|4={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||a}}s<!-- -->|6={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}|n}}a<!-- -->|8={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||u}}m{{#if:{{{vowel|}}}|,{{{2|{{{1}}}}}}um}}<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|masculine a-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> isjxxwnkyvpkwbqwfoaj1d1970v0a6y Module:checkparams 828 7955 54686 2026-09-25T16:41:11Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local compare_module = "Module:compare" local debug_module = "Module:debug" local maintenance_category_module = "Module:maintenance category" local parameters_module = "Module:parameters" local template_parser_module = "Module:template parser" local utilities_module = "Module:utilities" local concat = table.concat local get_current_title = mw.title.getCurrentTitle local html_create = mw.html.create local match = string.match local new_title =...' 54686 Scribunto text/plain local export = {} local compare_module = "Module:compare" local debug_module = "Module:debug" local maintenance_category_module = "Module:maintenance category" local parameters_module = "Module:parameters" local template_parser_module = "Module:template parser" local utilities_module = "Module:utilities" local concat = table.concat local get_current_title = mw.title.getCurrentTitle local html_create = mw.html.create local match = string.match local new_title = mw.title.new local next = next local pairs = pairs local require = require local select = select local sort = table.sort local tostring = tostring local type = type local function find_parameters(...) find_parameters = require(template_parser_module).find_parameters return find_parameters(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function formatted_error(...) formatted_error = require(debug_module).formatted_error return formatted_error(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function uses_hidden_category(...) uses_hidden_category = require(maintenance_category_module).uses_hidden_category return uses_hidden_category(...) end local compare local function get_compare() compare, get_compare = require(compare_module), nil return compare end -- Returns a table of all arguments in `template_args` which are not supported -- by `template_title` or listed in `additional`. local function get_invalid_args(template_title, template_args, additional) local content = template_title:getContent() if not content then -- This should only be possible if the input frame has been tampered with. error("Could not retrieve the page content of \"" .. template_title.prefixedText .. "\".") end local allowed_params, seen = {}, {} -- Detect all params used by the parent template. param:get_name() takes the -- parent frame arg table as an argument so that preprocessing will take -- them into account, since it will matter if the name contains another -- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change -- depending on the value for bar=). `seen` memoizes results based on the -- raw parameter text (which is stored as a string in the parameter object), -- which avoids unnecessary param:get_name() calls, which are non-trivial. for param in find_parameters(content) do local raw = param.raw if not seen[raw] then allowed_params[param:get_name(template_args)] = true seen[raw] = true end end -- Add any additional allowed arguments. if additional then local i = 0 while true do i = i + 1 local param = additional[i] if param == nil then break end allowed_params[param] = true end end local invalid_args = select(2, process_params( template_args, allowed_params, "return unknown" )) if not next(invalid_args) then return invalid_args end -- Some templates use params 1 and 3 without using 2, which means that 2 -- will be in the list of invalid args when used as an empty placeholder -- (e.g. {{foo|foo||bar}}). Detect and remove any empty positional -- placeholder args. local max_pos = 0 for param in pairs(allowed_params) do if type(param) == "number" and param > max_pos then max_pos = param end end for param, arg in pairs(invalid_args) do if ( type(param) == "number" and param >= 1 and param < max_pos and -- Ignore if arg is empty, or only contains chars trimmed by -- MediaWiki when handling named parameters. match(arg, "^[%z\t-\v\r ]*$") ) then invalid_args[param] = nil end end return invalid_args end -- Convert `args` into an array of sorted PARAM=ARG strings, using the parameter -- name as the sortkey, with numbered params sorted before strings. local function args_to_sorted_tuples(args) local msg, i = {}, 0 for k, v in pairs(args) do i = i + 1 msg[i] = {k, v} end sort(msg, compare or get_compare()) for j = 1, i do msg[j] = concat(msg[j], "=") end return msg end local function apply_pre_tag(frame, invalid_args) return frame:extensionTag("pre", concat(invalid_args, "\n")) end local function make_message(template_name, invalid_args, no_link) local open, close if no_link then open, close = "", "" else open, close = "[[", "]]" end return "The template " .. open .. template_name .. close .. " does not use the parameter(s): " .. invalid_args .. " Please see " .. open .. "Module:checkparams" .. close .. " for help with this warning." end -- Called by non-Lua templates using "{{#invoke:checkparams|warn}}". `frame` -- is checked for the following params: -- `1=` (optional) a comma separated list of additional allowed parameters -- `nowarn=` (optional) do not include preview warning in warning_text -- `noattn=` (optional) do not include attention seeking span in in warning_text function export.warn(frame) local parent = frame:getParent() local template_name = parent:getTitle() local template_title = new_title(template_name) local boolean = {type = "boolean"} local iargs = process_params(frame.args, { [1] = {type = "parameter", sublist = true}, ["nowarn"] = boolean, ["noattn"] = boolean, }) local invalid_args = get_invalid_args(template_title, parent.args, iargs[1]) -- If there are no invalid template args, return. if not next(invalid_args) then return "" end -- Otherwise, generate "Invalid params" warning to be inserted onto the -- wiki page. local warn, attn, cat invalid_args = args_to_sorted_tuples(invalid_args) -- Show warning in previewer. if not iargs.nowarn then warn = tostring(html_create("sup") :addClass("error") :addClass("previewonly") :tag("small") :wikitext(make_message(template_name, apply_pre_tag(frame, invalid_args))) :allDone()) end -- Add attentionseeking message. <pre> tags don't work in HTML attributes, -- so use semicolons as delimiters. if not iargs.noattn then attn = tostring(html_create("span") :addClass("attentionseeking") :attr("title", make_message(template_name, concat(invalid_args, "; ") .. ".", "no_link")) :allDone()) end -- Categorize if neither the current page nor the template would go in a hidden maintenance category. if not (uses_hidden_category(get_current_title()) or uses_hidden_category(template_title)) then cat = format_categories("Pages using invalid parameters when calling " .. template_name, nil, "-", nil, "force_output") end return (warn or "") .. (attn or "") .. (cat or "") end -- Called by non-Lua templates using "{{#invoke:checkparams|error}}". `frame` -- is checked for the following params: -- `1=` (optional) a comma separated list of additional allowed parameters function export.error(frame) local parent = frame:getParent() local template_name = parent:getTitle() local additional = process_params(frame.args, { [1] = {type = "parameter", sublist = true}, })[1] local invalid_args = get_invalid_args(new_title(template_name), parent.args, additional) -- Use formatted_error, so that we can use <pre> tags in error messages: -- any whitespace which isn't trimmed is treated as literal, so errors -- caused by double-spaces or erroneous newlines in inputs need to be -- displayed accurately. if next(invalid_args) then return formatted_error(make_message( template_name, apply_pre_tag(frame, args_to_sorted_tuples(invalid_args)) )) end end return export 1xni2b4rbvraa0aaafw8qfbytlvtlrp Module:template parser 828 7956 54687 2026-09-25T16:42:28Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[[ NOTE: This module works by using recursive backtracking to build a node tree, which can then be traversed as necessary. Because it is called by a number of high-use modules, it has been optimised for speed using a profiler, since it is used to scrape data from large numbers of pages very quickly. To that end, it rolls some of its own methods in cases where this is faster than using a function from one of the standard libraries. Please DO NOT "simplify" the...' 54687 Scribunto text/plain --[[ NOTE: This module works by using recursive backtracking to build a node tree, which can then be traversed as necessary. Because it is called by a number of high-use modules, it has been optimised for speed using a profiler, since it is used to scrape data from large numbers of pages very quickly. To that end, it rolls some of its own methods in cases where this is faster than using a function from one of the standard libraries. Please DO NOT "simplify" the code by removing these, since you are almost guaranteed to slow things down, which could seriously impact performance on pages which call this module hundreds or thousands of times. It has also been designed to emulate the native parser's behaviour as much as possible, which in some cases means replicating bugs or unintuitive behaviours in that code; these should not be "fixed", since it is important that the outputs are the same. Most of these originate from deficient regular expressions, which can't be used here, so the bugs have to be manually reintroduced as special cases (e.g. onlyinclude tags being case-sensitive and whitespace intolerant, unlike all other tags). If any of these are fixed, this module should also be updated accordingly. ]] local export = {} local data_module = "Module:template parser/data" local load_module = "Module:load" local magic_words_data_module = "Module:data/magic words" local pages_module = "Module:pages" local parser_extension_tags_data_module = "Module:data/parser extension tags" local parser_module = "Module:parser" local scribunto_module = "Module:Scribunto" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_length_module = "Module:table/length" local table_shallow_copy_module = "Module:table/shallowCopy" local table_sorted_pairs_module = "Module:table/sortedPairs" local title_is_title_module = "Module:title/isTitle" local title_make_title_module = "Module:title/makeTitle" local title_new_title_module = "Module:title/newTitle" local title_redirect_target_module = "Module:title/redirectTarget" local require = require local m_parser = require(parser_module) local mw = mw local mw_title = mw.title local mw_uri = mw.uri local string = string local table = table local anchor_encode = mw_uri.anchorEncode local build_template -- defined as export.buildTemplate below local class_else_type = m_parser.class_else_type local concat = table.concat local encode_uri = mw_uri.encode local find = string.find local format = string.format local gsub = string.gsub local html_create = mw.html.create local insert = table.insert local is_node = m_parser.is_node local lower = string.lower local match = string.match local next = next local pairs = pairs local parse -- defined as export.parse below local parse_template_name -- defined below local pcall = pcall local rep = string.rep local select = select local sub = string.sub local title_equals = mw_title.equals local tostring = m_parser.tostring local type = type local umatch = mw.ustring.match --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function get_link_target(...) get_link_target = require(pages_module).get_link_target return get_link_target(...) end local function is_title(...) is_title = require(title_is_title_module) return is_title(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function make_title(...) make_title = require(title_make_title_module) return make_title(...) end local function new_title(...) new_title = require(title_new_title_module) return new_title(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function php_htmlspecialchars(...) php_htmlspecialchars = require(scribunto_module).php_htmlspecialchars return php_htmlspecialchars(...) end local function php_ltrim(...) php_ltrim = require(scribunto_module).php_ltrim return php_ltrim(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function redirect_target(...) redirect_target = require(title_redirect_target_module) return redirect_target(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function shallow_copy(...) shallow_copy = require(table_shallow_copy_module) return shallow_copy(...) end local function sorted_pairs(...) sorted_pairs = require(table_sorted_pairs_module) return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function table_len(...) table_len = require(table_length_module) return table_len(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local data local function get_data() data, get_data = load_data(data_module), nil return data end local frame local function get_frame() frame, get_frame = mw.getCurrentFrame(), nil return frame end local magic_words local function get_magic_words() magic_words, get_magic_words = load_data(magic_words_data_module), nil return magic_words end local parser_extension_tags local function get_parser_extension_tags() parser_extension_tags, get_parser_extension_tags = load_data(parser_extension_tags_data_module), nil return parser_extension_tags end ------------------------------------------------------------------------------------ -- -- Nodes -- ------------------------------------------------------------------------------------ local Node = m_parser.node() local new_node = Node.new local function expand(obj, frame_args) return is_node(obj) and obj:expand(frame_args) or obj end export.expand = expand function Node:expand(frame_args) local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return concat(output) end local Wikitext = Node:new_class("wikitext") -- force_node ensures the output will always be a Wikitext node. function Wikitext:new(this, force_node) if type(this) ~= "table" then return force_node and new_node(self, {this}) or this elseif #this == 1 then local this1 = this[1] return force_node and class_else_type(this1) ~= "wikitext" and new_node(self, this) or this1 end local success, str = pcall(concat, this) if success then return force_node and new_node(self, {str}) or str end return new_node(self, this) end -- First value is the parameter name. -- Second value is the parameter's default value. -- Any additional values are ignored: e.g. "{{{a|b|c}}}" is parameter "a" with default value "b" (*not* "b|c"). local Parameter = Node:new_class("parameter") function Parameter:new(this) local this2 = this[2] if class_else_type(this2) == "argument" then insert(this2, 2, "=") this2 = Wikitext:new(this2) end if this[3] == nil then this[2] = this2 else this = {this[1], this2} end return new_node(self, this) end function Parameter:__tostring() local output = {} for i = 1, #self do output[i] = tostring(self[i]) end return "{{{" .. concat(output, "|") .. "}}}" end function Parameter:get_name(frame_args) return scribunto_parameter_key(expand(self[1], frame_args)) end function Parameter:get_default(frame_args) local default = self[2] if default ~= nil then return expand(default, frame_args) end return "{{{" .. expand(self[1], frame_args) .. "}}}" end function Parameter:expand(frame_args) if frame_args == nil then return self:get_default() end local name = expand(self[1], frame_args) local val = frame_args[scribunto_parameter_key(name)] -- Parameter in use. if val ~= nil then return val end val = self[2] -- Default. if val ~= nil then return expand(val, frame_args) end return "{{{" .. name .. "}}}" end local Argument = Node:new_class("argument") function Argument:new(this) local key = this._parse_data.key this = Wikitext:new(this) if key == nil then return this end return new_node(self, {Wikitext:new(key), this}) end function Argument:__tostring() return tostring(self[1]) .. "=" .. tostring(self[2]) end function Argument:expand(frame_args) return expand(self[1], frame_args) .. "=" .. expand(self[2], frame_args) end local Template = Node:new_class("template") function Template:__tostring() local output = {} for i = 1, #self do output[i] = tostring(self[i]) end return "{{" .. concat(output, "|") .. "}}" end -- Normalize the template name, check it's a valid template, then memoize results (using false for invalid titles). -- Parser functions (e.g. {{#IF:a|b|c}}) need to have the first argument extracted from the title, as it comes after the colon. Because of this, the parser function and first argument are memoized as a table. -- FIXME: Some parser functions have special argument handling (e.g. {{#SWITCH:}}). do local templates, parser_variables, parser_functions = {}, {}, {} local function retrieve_magic_word_data(chunk) local mgw_data = (magic_words or get_magic_words())[chunk] if mgw_data then return mgw_data end local normalized = uupper(chunk) mgw_data = magic_words[normalized] if mgw_data and not mgw_data.case_sensitive then return mgw_data end end -- Returns the name required to transclude the title object `title` using -- template {{ }} syntax. If the `shortcut` flag is set, then any calls -- which require a namespace prefix will use the abbreviated form where one -- exists (e.g. "Template:PAGENAME" becomes "T:PAGENAME"). local function get_template_invocation_name(title, shortcut) if not (is_title(title) and not title.isExternal) then error("Template invocations require a valid page title, which cannot contain an interwiki prefix.") end local namespace = title.namespace -- If not in the template namespace, include the prefix (or ":" if -- mainspace). if namespace ~= 10 then return get_link_target(title, shortcut) end -- If in the template namespace and it shares a name with a magic word, -- it needs the prefix "Template:". local text, fragment = title.text, title.fragment if fragment and fragment ~= "" then text = text .. "#" .. fragment end local colon = find(text, ":", nil, true) if not colon then local mgw_data = retrieve_magic_word_data(text) return mgw_data and mgw_data.parser_variable and get_link_target(title, shortcut) or text end local mgw_data = retrieve_magic_word_data(sub(text, 1, colon - 1)) if mgw_data and (mgw_data.parser_function or mgw_data.transclusion_modifier) then return get_link_target(title, shortcut) end -- Also if "Template:" is necessary for disambiguation (e.g. -- "Template:Category:Foo" can't be called with "Category:Foo"). local check = new_title(text, namespace) return check and title_equals(title, check) and text or get_link_target(title, shortcut) end export.getTemplateInvocationName = get_template_invocation_name function parse_template_name(name, has_args, fragment, force_transclusion) local chunks, colon, start, n, p = {}, find(name, ":", nil, true), 1, 0, 0 while colon do local mgw_data = retrieve_magic_word_data(php_ltrim(sub(name, start, colon - 1))) if not mgw_data then break end local priority = mgw_data.priority if not (priority and priority > p) then local pf = mgw_data.parser_function and mgw_data.name or nil if pf then n = n + 1 chunks[n] = pf .. ":" return chunks, "parser function", sub(name, colon + 1) end break end n = n + 1 chunks[n] = mgw_data.name .. ":" start, p = colon + 1, priority colon = find(name, ":", start, true) end if start > 1 then name = sub(name, start) end name = php_trim(name) -- Parser variables can only take SUBST:/SAFESUBST: as modifiers. if not has_args and p <= 1 then local mgw_data = retrieve_magic_word_data(name) local pv = mgw_data and mgw_data.parser_variable and mgw_data.name or nil if pv then n = n + 1 chunks[n] = pv return chunks, "parser variable" end end -- Get the template title with the custom new_title() function in -- [[Module:title/newTitle]], with `allowOnlyFragment` set to false -- (e.g. "{{#foo}}" is invalid) and `allowRelative` set to true, for -- relative links for namespaces with subpages (e.g. "{{/foo}}"). local title = new_title(name, 10, false, true) if not (title and not title.isExternal) then return nil end if force_transclusion ~= "no_redirect" then -- Resolve any redirects. If the redirect target is an interwiki link, -- the template won't fail, but the redirect does not get resolved (i.e. -- the redirect page itself gets transcluded, so the template name -- should not be normalized to the target). local redirect = redirect_target(title, force_transclusion) if redirect and not redirect.isExternal then title = redirect end end -- If `fragment` is not true, unset it from the title object to prevent -- it from being included by get_template_invocation_name. if not fragment then title.fragment = "" end chunks[n + 1] = get_template_invocation_name(title) return chunks, "template" end -- Note: `force_transclusion` avoids incrementing the expensive parser function count by forcing transclusion -- instead. This should only be used when there is a real risk that the expensive parser function limit of -- 500 will be hit. If `force_transclusion` has the value "no_redirect", redirect checking is turned off -- entirely. This should only be used in very limited circumstances when it is otherwise impossible to avoid -- hitting the expensive parser limit and we are willing to handle the possible redirects ourselves. local function process_name(self, frame_args, force_transclusion) local name = expand(self[1], frame_args) local has_args, norm = #self > 1 if not has_args then norm = parser_variables[name] if norm then return norm, "parser variable" end end norm = templates[name] if norm then local pf_arg1 = parser_functions[name] return norm, pf_arg1 and "parser function" or "template", pf_arg1 elseif norm == false then return nil end local chunks, subclass, pf_arg1 = parse_template_name(name, has_args, nil, force_transclusion) -- Fail if invalid. if not chunks then templates[name] = false return nil end local chunk1 = chunks[1] -- Fail on SUBST:. if chunk1 == "SUBST:" then templates[name] = false return nil -- Any modifiers are ignored. elseif subclass == "parser function" then local pf = chunks[#chunks] templates[name] = pf parser_functions[name] = pf_arg1 return pf, "parser function", pf_arg1 end -- Ignore SAFESUBST:, and treat MSGNW: as a parser function with the pagename as its first argument (ignoring any RAW: that comes after). if chunks[chunk1 == "SAFESUBST:" and 2 or 1] == "MSGNW:" then pf_arg1 = chunks[#chunks] local pf = "MSGNW:" templates[name] = pf parser_functions[name] = pf_arg1 return pf, "parser function", pf_arg1 end -- Ignore any remaining modifiers, as they've done their job. local output = chunks[#chunks] if subclass == "parser variable" then parser_variables[name] = output else templates[name] = output end return output, subclass end function Template:get_name(frame_args, force_transclusion) -- Only return the first return value. return (process_name(self, frame_args, force_transclusion)) end function Template:get_arguments(frame_args) local name, subclass, pf_arg1 = process_name(self, frame_args) if name == nil then return nil elseif subclass == "parser variable" then return {} end local template_args = {} if subclass == "parser function" then template_args[1] = pf_arg1 for i = 2, #self do template_args[i] = expand(self[i], frame_args) -- Not trimmed. end return template_args end local implicit = 0 for i = 2, #self do local arg = self[i] if class_else_type(arg) == "argument" then template_args[scribunto_parameter_key(expand(arg[1], frame_args))] = php_trim((expand(arg[2], frame_args))) else implicit = implicit + 1 template_args[implicit] = expand(arg, frame_args) -- Not trimmed. end end return template_args end -- BIG TODO: manual template expansion. function Template:expand(frame_args) local name, subclass, pf_arg1 = process_name(self, frame_args) if name == nil then local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return "{{" .. concat(output, "|") .. "}}" elseif subclass == "parser variable" then return (frame or get_frame()):preprocess("{{" .. name .. "}}") elseif subclass == "parser function" then local f = frame or get_frame() if frame_args ~= nil then local success, new_f = pcall(f.newChild, f, {args = frame_args}) if success then f = new_f end end return f:preprocess(tostring(self)) end local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return (frame or get_frame()):preprocess("{{" .. concat(output, "|") .. "}}") end end local Tag = Node:new_class("tag") function Tag:__tostring() local open_tag, attributes, n = {"<", self.name}, self:get_attributes(), 2 for attr, value in next, attributes do n = n + 1 open_tag[n] = " " .. php_htmlspecialchars(attr) .. "=\"" .. php_htmlspecialchars(value, "compat") .. "\"" end if self.self_closing then return concat(open_tag) .. "/>" end return concat(open_tag) .. ">" .. concat(self) .. "</" .. self.name .. ">" end do local valid_attribute_name local function get_valid_attribute_name() valid_attribute_name, get_valid_attribute_name = (data or get_data()).valid_attribute_name, nil return valid_attribute_name end function Tag:get_attributes() local raw = self.attributes if not raw then self.attributes = {} return self.attributes elseif type(raw) == "table" then return raw end if sub(raw, -1) == "/" then raw = sub(raw, 1, -2) end local attributes, head = {}, 1 -- Semi-manual implementation of the native regex. while true do local name, loc = match(raw, "([^\t\n\f\r />][^\t\n\f\r /=>]*)()", head) if not name then break end head = loc local value loc = match(raw, "^[\t\n\f\r ]*=[\t\n\f\r ]*()", head) if loc then head = loc -- Either "", '' or the value ends on a space/at the end. Missing -- end quotes are repaired by closing the value at the end. value, loc = match(raw, "^\"([^\"]*)\"?()", head) if not value then value, loc = match(raw, "^'([^']*)'?()", head) if not value then value, loc = match(raw, "^([^\t\n\f\r ]*)()", head) end end head = loc end -- valid_attribute_name is a pattern matching a valid attribute name. -- Defined in the data due to its length - see there for more info. if umatch(name, valid_attribute_name or get_valid_attribute_name()) then -- Sanitizer applies PHP strtolower (ASCII-only). attributes[lower(name)] = value and decode_entities( php_trim((gsub(value, "[\t\n\r ]+", " "))) ) or "" end end self.attributes = attributes return attributes end end function Tag:expand() return (frame or get_frame()):preprocess(tostring(self)) end local Heading = Node:new_class("heading") function Heading:new(this) if #this > 1 then local success, str = pcall(concat, this) if success then return new_node(self, { str, level = this.level, section = this.section, index = this.index }) end end return new_node(self, this) end do local node_tostring = Node.__tostring function Heading:__tostring() local eq = rep("=", self.level) return eq .. node_tostring(self) .. eq end end do local expand_node = Node.expand -- Expanded heading names can contain "\n" (e.g. inside nowiki tags), which -- causes any heading containing them to fail. However, in such cases, the -- native parser still treats it as a heading for the purpose of section -- numbers. local function validate_name(self, frame_args) local name = expand_node(self, frame_args) if find(name, "\n", nil, true) then return nil end return name end function Heading:get_name(frame_args) local name = validate_name(self, frame_args) return name ~= nil and php_trim(name) or nil end -- FIXME: account for anchor disambiguation. function Heading:get_anchor(frame_args) local name = validate_name(self, frame_args) return name ~= nil and decode_entities(anchor_encode(name)) or nil end function Heading:expand(frame_args) local eq = rep("=", self.level) return eq .. expand_node(self, frame_args) .. eq end end ------------------------------------------------------------------------------------ -- -- Parser -- ------------------------------------------------------------------------------------ local Parser = m_parser.string_parser() -- Template or parameter. -- Parsed by matching the opening braces innermost-to-outermost (ignoring lone closing braces). Parameters {{{ }}} take priority over templates {{ }} where possible, but a double closing brace will always result in a closure, even if there are 3+ opening braces. -- For example, "{{{{foo}}}}" (4) is parsed as a parameter enclosed by single braces, and "{{{{{foo}}}}}" (5) is a parameter inside a template. However, "{{{{{foo }} }}}" is a template inside a parameter, due to "}}" forcing the closure of the inner node. do -- Handlers. local handle_name local handle_argument local handle_value local function do_template_or_parameter(self, inner_node) self:push_sublayer(handle_name) self:set_pattern("[\n<[{|}]") -- If a node has already been parsed, nest it at the start of the new -- outer node (e.g. when parsing"{{{{foo}}bar}}", the template "{{foo}}" -- is parsed first, since it's the innermost, and becomes the first -- node of the outer template. if inner_node then self:emit(inner_node) end end local function pipe(self) self:emit(Wikitext:new(self:pop_sublayer())) self:push_sublayer(handle_argument) self:set_pattern("[\n<=[{|}]") end local function rbrace(self, this) if self:read(1) == "}" then self:emit(Wikitext:new(self:pop_sublayer())) return self:pop() end self:emit(this) end function handle_name(self, ...) handle_name = self:switch(handle_name, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = pipe, ["}"] = rbrace, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_name(self, ...) end function handle_argument(self, ...) handle_argument = self:switch(handle_argument, { ["\n"] = function(self, this) return self:heading_block(this, "==") end, ["<"] = Parser.tag, ["="] = function(self) local key = self:pop_sublayer() self:push_sublayer(handle_value) self:set_pattern("[\n<[{|}]") self.current_layer._parse_data.key = key end, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = pipe, ["}"] = rbrace, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_argument(self, ...) end function handle_value(self, ...) handle_value = self:switch(handle_value, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = function(self) self:emit(Argument:new(self:pop_sublayer())) self:push_sublayer(handle_argument) self:set_pattern("[\n<=[{|}]") end, ["}"] = function(self, this) if self:read(1) == "}" then self:emit(Argument:new(self:pop_sublayer())) return self:pop() end self:emit(this) end, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_value(self, ...) end function Parser:template_or_parameter() local text, head, node_to_emit, failed = self.text, self.head -- Comments/tags interrupt the brace count. local braces = match(text, "^{+()", head) - head self:advance(braces) while true do local success, node = self:try(do_template_or_parameter, node_to_emit) -- Fail means no "}}" or "}}}" was found, so emit any remaining -- unmatched opening braces before any templates/parameters that -- were found. if not success then self:emit(rep("{", braces)) failed = true break -- If there are 3+ opening and closing braces, it's a parameter. elseif braces >= 3 and self:read(2) == "}" then self:advance(3) braces = braces - 3 node = Parameter:new(node) -- Otherwise, it's a template. else self:advance(2) braces = braces - 2 node = Template:new(node) end local index = head + braces node.index = index node.raw = sub(text, index, self.head - 1) node_to_emit = node -- Terminate once not enough braces remain for further matches. if braces == 0 then break -- Emit any stray opening brace before any matched nodes. elseif braces == 1 then self:emit("{") break end end if node_to_emit then self:emit(node_to_emit) end return braces, failed end end -- Tag. do local end_tags local function get_end_tags() end_tags, get_end_tags = (data or get_data()).end_tags, nil return end_tags end -- Handlers. local handle_start local handle_tag local function do_tag(self) local layer = self.current_layer layer._parse_data.handler, layer.index = handle_start, self.head self:set_pattern("[%s/>]") self:advance() end local function is_ignored_tag(self, this) if self.transcluded then return this == "includeonly" end return this == "noinclude" or this == "onlyinclude" end local function ignored_tag(self, text, head) local loc = find(text, ">", head, true) if not loc then return self:fail_route() end self:jump(loc) local tag = self:pop() tag.ignored = true return tag end function handle_start(self, this) if this == "/" then local text, head = self.text, self.head + 1 local this = match(text, "^[^%s/>]+", head) if this and is_ignored_tag(self, lower(this)) then head = head + #this if not match(text, "^/[^>]", head) then return ignored_tag(self, text, head) end end return self:fail_route() elseif this == "" then return self:fail_route() end -- Tags are only case-insensitive with ASCII characters. local raw_name = this this = lower(this) local end_tag_pattern = (end_tags or get_end_tags())[this] if not end_tag_pattern then -- Validity check. return self:fail_route() end local layer = self.current_layer local pdata = layer._parse_data local text, head = self.text, self.head + pdata.step if match(text, "^/[^>]", head) then return self:fail_route() elseif is_ignored_tag(self, this) then return ignored_tag(self, text, head) -- If an onlyinclude tag is not ignored (and cannot be active since it -- would have triggered special handling earlier), it must be plaintext. elseif this == "onlyinclude" then return self:fail_route() elseif this == "noinclude" or this == "includeonly" then layer.ignored = true -- Ignored block. layer.raw_name = raw_name end layer.name, pdata.handler, pdata.end_tag_pattern = this, handle_tag, end_tag_pattern self:set_pattern(">") end function handle_tag(self, this) if this == "" then return self:fail_route() end local layer = self.current_layer if this ~= ">" then layer.attributes = this return elseif self:read(-1) == "/" then layer.self_closing = true return self:pop() end local text, head = self.text, self.head + 1 local loc1, loc2 = find(text, layer._parse_data.end_tag_pattern, head) if loc1 then if loc1 > head then self:emit(sub(text, head, loc1 - 1)) end self:jump(loc2) return self:pop() -- noinclude and includeonly will tolerate having no closing tag, but -- only if given in lowercase. This is due to a preprocessor bug, as -- it uses a regex with the /i (case-insensitive) flag to check for -- end tags, but a simple array lookup with lowercase tag names when -- looking up which tags should tolerate no closing tag (exact match -- only, so case-sensitive). elseif layer.ignored then local raw_name = layer.raw_name if raw_name == "noinclude" or raw_name == "includeonly" then self:jump(#text) return self:pop() end end return self:fail_route() end function Parser:tag() -- HTML comment. if self:read(1, 3) == "!--" then local text = self.text self:jump(select(2, find(text, "-->", self.head + 4, true)) or #text) -- onlyinclude tags (which must be lowercase with no whitespace). elseif self.onlyinclude and self:read(1, 13) == "/onlyinclude>" then local text = self.text self:jump(select(2, find(text, "<onlyinclude>", self.head + 14, true)) or #text) else local success, tag = self:try(do_tag) if not success then self:emit("<") elseif not tag.ignored then self:emit(Tag:new(tag)) end end end end -- Heading. -- The preparser assigns each heading a number, which is used for things like section edit links. The preparser will only do this for heading blocks which aren't nested inside templates, parameters and parser tags. In some cases (e.g. when template blocks contain untrimmed newlines), a preparsed heading may not be treated as a heading in the final output. That does not affect the preparser, however, which will always count sections based on the preparser heading count, since it can't know what a template's final output will be. do -- Handlers. local handle_start local handle_body local handle_possible_end local function do_heading(self) local layer, head = self.current_layer, self.head layer._parse_data.handler, layer.index = handle_start, head self:set_pattern("[\t\n ]") -- Comments/tags interrupt the equals count. local eq = match(self.text, "^=+()", head) - head layer.level = eq self:advance(eq) end local function do_heading_possible_end(self) self.current_layer._parse_data.handler = handle_possible_end self:set_pattern("[\n<]") end function handle_start(self, ...) -- ===== is "=" as an L2; ======== is "==" as an L3 etc. local function newline(self) local layer = self.current_layer local eq = layer.level if eq <= 2 then return self:fail_route() end -- Calculate which equals signs determine the heading level. local level_eq = eq - (2 - eq % 2) level_eq = level_eq > 12 and 12 or level_eq -- Emit the excess. self:emit(rep("=", eq - level_eq)) layer.level = level_eq / 2 return self:pop() end local function whitespace(self) local success, possible_end = self:try(do_heading_possible_end) if success then self:emit(Wikitext:new(possible_end)) self.current_layer._parse_data.handler = handle_body self:set_pattern("[\n<=[{]") return self:consume() end return newline(self) end handle_start = self:switch(handle_start, { ["\t"] = whitespace, ["\n"] = newline, [" "] = whitespace, [""] = newline, [false] = function(self) -- Emit any excess = signs once we know it's a conventional heading. Up till now, we couldn't know if the heading is just a string of = signs (e.g. ========), so it wasn't guaranteed that the heading text starts after the 6th. local layer = self.current_layer local eq = layer.level if eq > 6 then self:emit(1, rep("=", eq - 6)) layer.level = 6 end layer._parse_data.handler = handle_body self:set_pattern("[\n<=[{]") return self:consume() end }) return handle_start(self, ...) end function handle_body(self, ...) handle_body = self:switch(handle_body, { ["\n"] = Parser.fail_route, ["<"] = Parser.tag, ["="] = function(self) -- Comments/tags interrupt the equals count. local eq = match(self.text, "^=+", self.head) local eq_len = #eq self:advance(eq_len) local success, possible_end = self:try(do_heading_possible_end) if success then self:emit(eq) self:emit(Wikitext:new(possible_end)) return self:consume() end local layer = self.current_layer local level = layer.level if eq_len > level then self:emit(rep("=", eq_len - level)) elseif level > eq_len then layer.level = eq_len self:emit(1, rep("=", level - eq_len)) end return self:pop() end, ["["] = Parser.wikilink_block, ["{"] = function(self, this) return self:braces(this, true) end, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_body(self, ...) end function handle_possible_end(self, ...) handle_possible_end = self:switch(handle_possible_end, { ["\n"] = Parser.fail_route, ["<"] = function(self) if self:read(1, 3) ~= "!--" then return self:pop() end local head = select(2, find(self.text, "-->", self.head + 4, true)) if not head then return self:pop() end self:jump(head) end, [""] = Parser.fail_route, [false] = function(self, this) if not match(this, "^[\t ]+()$") then return self:pop() end self:emit(this) end }) return handle_possible_end(self, ...) end function Parser:heading() local success, heading = self:try(do_heading) if success then local section = self.section + 1 heading.section = section self.section = section self:emit(Heading:new(heading)) return self:consume() else self:emit("=") end end end ------------------------------------------------------------------------------------ -- -- Block handlers -- ------------------------------------------------------------------------------------ -- Block handlers. -- These are blocks which can affect template/parameter parsing, since they're also parsed by Parsoid at the same time (even though they aren't processed until later). -- All blocks (including templates/parameters) can nest inside each other, but an inner block must be closed before the outer block which contains it. This is why, for example, the wikitext "{{template| [[ }}" will result in an unprocessed template, since the inner "[[" is treated as the opening of a wikilink block, which prevents "}}" from being treated as the closure of the template block. On the other hand, "{{template| [[ ]] }}" will process correctly, since the wikilink block is closed before the template closure. It makes no difference whether the block will be treated as valid or not when it's processed later on, so "{{template| [[ }} ]] }}" would also work, even though "[[ }} ]]" is not a valid wikilink. -- Note that nesting also affects pipes and equals signs, in addition to block closures. -- These blocks can be nested to any degree, so "{{template| [[ [[ [[ ]] }}" will not work, since only one of the three wikilink blocks has been closed. On the other hand, "{{template| [[ [[ [[ ]] ]] ]] }}" will work. -- All blocks are implicitly closed by the end of the text, since their validity is irrelevant at this stage. -- Language conversion block. -- Opens with "-{" and closes with "}-". However, templates/parameters take priority, so "-{{" is parsed as "-" followed by the opening of a template/parameter block (depending on what comes after). -- Note: Language conversion blocks aren't actually enabled on the English Wiktionary, but Parsoid still parses them at this stage, so they can affect the closure of outer blocks: e.g. "[[ -{ ]]" is not a valid wikilink block, since the "]]" falls inside the new language conversion block. do --Handler. local handle_language_conversion_block local function do_language_conversion_block(self) self.current_layer._parse_data.handler = handle_language_conversion_block self:set_pattern("[\n<[{}]") end function handle_language_conversion_block(self, ...) handle_language_conversion_block = self:switch(handle_language_conversion_block, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["}"] = function(self, this) if self:read(1) == "-" then self:emit("}-") self:advance() return self:pop() end self:emit(this) end, [""] = Parser.pop, [false] = Parser.emit }) return handle_language_conversion_block(self, ...) end function Parser:braces(this, fail_on_unclosed_braces) local language_conversion_block = self:read(-1) == "-" if self:read(1) == "{" then local braces, failed = self:template_or_parameter() -- Headings will fail if they contain an unclosed brace block. if failed and fail_on_unclosed_braces then return self:fail_route() -- Language conversion blocks cannot begin "-{{", but can begin -- "-{{{" iff parsed as "-{" + "{{". elseif not (language_conversion_block and braces == 1) then return self:consume() end else self:emit(this) if not language_conversion_block then return end self:advance() end self:emit(Wikitext:new(self:get(do_language_conversion_block))) end end --[==[ Headings Opens with "\n=" (or "=" at the start of the text), and closes with "\n" or the end of the text. Note that it doesn't matter whether the heading will fail to process due to a premature newline (e.g. if there are no closing signs), so at this stage the only thing that matters for closure is the newline or end of text. Note: Heading blocks are only parsed like this if they occur inside a template, since they do not iterate the preparser's heading count (i.e. they aren't proper headings). Note 2: if directly inside a template argument with no previous equals signs, a newline followed by a single equals sign is parsed as an argument equals sign, not the opening of a new L1 heading block. This does not apply to any other heading levels. As such, {{template|key\n=}}, {{template|key\n=value}} or even {{template|\n=}} will successfully close, but {{template|key\n==}}, {{template|key=value\n=more value}}, {{template\n=}} etc. will not, since in the latter cases the "}}" would fall inside the new heading block. ]==] do --Handler. local handle_heading_block local function do_heading_block(self) self.current_layer._parse_data.handler = handle_heading_block self:set_pattern("[\n<[{]") end function handle_heading_block(self, ...) handle_heading_block = self:switch(handle_heading_block, { ["\n"] = function(self) self:newline() return self:pop() end, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, [""] = Parser.pop, [false] = Parser.emit }) return handle_heading_block(self, ...) end function Parser:heading_block(this, nxt) self:newline() this = this .. (nxt or "=") local loc = #this - 1 while self:read(0, loc) == this do self:advance() self:emit(Wikitext:new(self:get(do_heading_block))) end end end -- Wikilink block. -- Opens with "[[" and closes with "]]". do -- Handler. local handle_wikilink_block local function do_wikilink_block(self) self.current_layer._parse_data.handler = handle_wikilink_block self:set_pattern("[\n<[%]{]") end function handle_wikilink_block(self, ...) handle_wikilink_block = self:switch(handle_wikilink_block, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["]"] = function(self, this) if self:read(1) == "]" then self:emit("]]") self:advance() return self:pop() end self:emit(this) end, ["{"] = Parser.braces, [""] = Parser.pop, [false] = Parser.emit }) return handle_wikilink_block(self, ...) end function Parser:wikilink_block() if self:read(1) == "[" then self:emit("[[") self:advance(2) self:emit(Wikitext:new(self:get(do_wikilink_block))) else self:emit("[") end end end -- Lines which only contain comments, " " and "\t" are eaten, so long as -- they're bookended by "\n" (i.e. not the first or last line). function Parser:newline() local text, head = self.text, self.head while true do repeat local loc = match(text, "^[\t ]*<!%-%-()", head + 1) if not loc then break end loc = select(2, find(text, "-->", loc, true)) head = loc or head until not loc -- Fail if no comments found. if head == self.head then break end head = match(text, "^[\t ]*()\n", head + 1) if not head then break end self:jump(head) end self:emit("\n") end do -- Handlers. local handle_start local main_handler -- If `transcluded` is true, then the text is checked for a pair of -- onlyinclude tags. If these are found (even if they're in the wrong -- order), then the start of the page is treated as though it is preceded -- by a closing onlyinclude tag. -- Note 1: unlike other parser extension tags, onlyinclude tags are case- -- sensitive and cannot contain whitespace. -- Note 2: onlyinclude tags *can* be implicitly closed by the end of the -- text, but the hard requirement above means this can only happen if -- either the tags are in the wrong order or there are multiple onlyinclude -- blocks. local function do_parse(self, transcluded) self.current_layer._parse_data.handler = handle_start self:set_pattern(".") self.section = 0 if not transcluded then return end self.transcluded = true local text = self.text if find(text, "</onlyinclude>", nil, true) then local head = find(text, "<onlyinclude>", nil, true) if head then self.onlyinclude = true self:jump(head + 13) end end end -- If the first character is "=", try parsing it as a heading. function handle_start(self, this) self.current_layer._parse_data.handler = main_handler self:set_pattern("[\n<{]") if this == "=" then return self:heading() end return self:consume() end function main_handler(self, ...) main_handler = self:switch(main_handler, { ["\n"] = function(self) self:newline() if self:read(1) == "=" then self:advance() return self:heading() end end, ["<"] = Parser.tag, ["{"] = function(self, this) if self:read(1) == "{" then self:template_or_parameter() return self:consume() end self:emit(this) end, [""] = Parser.pop, [false] = Parser.emit }) return main_handler(self, ...) end function export.parse(text, transcluded) local text_type = type(text) return (select(2, Parser:parse{ text = text_type == "string" and text or text_type == "number" and tostring(text) or error("bad argument #1 (string expected, got " .. text_type .. ")"), node = {Wikitext, true}, route = {do_parse, transcluded} })) end parse = export.parse end function export.find_templates(text, not_transcluded) return parse(text, not not_transcluded):iterate_nodes("template") end do local link_parameter_1, link_parameter_2 local function get_link_parameter_1() link_parameter_1, get_link_parameter_1 = (data or get_data()).template_link_param_1, nil return link_parameter_1 end local function get_link_parameter_2() link_parameter_2, get_link_parameter_2 = (data or get_data()).template_link_param_2, nil return link_parameter_2 end -- Generate a link. If the target title doesn't have a fragment, use "#top" -- (which is an implicit anchor at the top of every page), as this ensures -- self-links still display as links, since bold display is distracting and -- unintuitive for template links. local function link_page(title, display) local fragment = title.fragment if fragment == "" then fragment = "top" end return format( "[[:%s|%s]]", encode_uri(title.prefixedText .. "#" .. fragment, "WIKI"), display ) end -- pf_arg1 or pf_arg2 may need to be linked if a given parser function -- treats them as a pagename. If a key exists in `namespace`, the value is -- the namespace for the page: if not 0, then the namespace prefix will -- always be added to the input (e.g. {{#invoke:}} can only target the -- Module: namespace, so inputting "Template:foo" gives -- "Module:Template:foo", and "Module:foo" gives "Module:Module:foo"). -- However, this isn't possible with mainspace (namespace 0), so prefixes -- are respected. make_title() handles all of this automatically. local function finalize_arg(pagename, namespace) if namespace == nil then return pagename end local title = make_title(namespace, pagename) return title and not title.isExternal and link_page(title, pagename) or pagename end local function render_title(name, args) -- parse_template_name returns a table of transclusion modifiers plus -- the normalized template/magic word name, which will be used as link -- targets. The third return value pf_arg1 is the first argument of a -- a parser function, which comes after the colon (e.g. "foo" in -- "{{#IF:foo|bar|baz}}"). This means args[1] (i.e. the first argument -- that comes after a pipe is actually argument 2, and so on. Note: the -- second parameter of parse_template_name checks if there are any -- arguments, since parser variables cannot take arguments (e.g. -- {{CURRENTYEAR}} is a parser variable, but {{CURRENTYEAR|foo}} -- transcludes "Template:CURRENTYEAR"). In such cases, the returned -- table explicitly includes the "Template:" prefix in the template -- name. The third parameter instructs it to retain any fragment in the -- template name in the returned table, if present. local chunks, subclass, pf_arg1 = parse_template_name( name, args and pairs(args)(args) ~= nil, true ) if chunks == nil then return name, args end local chunks_len = #chunks -- Additionally, generate the corresponding table `rawchunks`, which -- is a list of colon-separated chunks in the raw input. This is used -- to retrieve the display forms for each chunk. local rawchunks = split(name, ":") for i = 1, chunks_len - 1 do chunks[i] = format( "[[%s|%s]]", encode_uri((magic_words or get_magic_words())[sub(chunks[i], 1, -2)].transclusion_modifier, "WIKI"), rawchunks[i] ) end local chunk = chunks[chunks_len] -- If it's a template, return a link to it with link_page, concatenating -- the remaining chunks in `rawchunks` to form the display text. -- Use new_title with the default namespace 10 (Template:) to generate -- a target title, which is the same setting used for retrieving -- templates (including those in other namespaces, as prefixes override -- the default). if subclass == "template" then chunks[chunks_len] = link_page( new_title(chunk, 10), concat(rawchunks, "&#58;", chunks_len) -- : ) return concat(chunks, "&#58;"), args -- : elseif subclass == "parser variable" then chunks[chunks_len] = format( "[[%s|%s]]", encode_uri((magic_words or get_magic_words())[chunk].parser_variable, "WIKI"), rawchunks[chunks_len] ) return concat(chunks, "&#58;"), args -- : end -- Otherwise, it must be a parser function. local mgw_data = (magic_words or get_magic_words())[sub(chunk, 1, -2)] local link = mgw_data.parser_function or mgw_data.transclusion_modifier local pf_arg2 = args and args[1] or nil -- Some magic words have different links, depending on whether argument -- 2 is specified (e.g. "baz" in {{foo:bar|baz}}). if type(link) == "table" then link = pf_arg2 and link[2] or link[1] end chunks[chunks_len] = format( "[[%s|%s]]", encode_uri(link, "WIKI"), rawchunks[chunks_len] ) -- #TAG: has special handling, because documentation links for parser -- extension tags come from [[Module:data/parser extension tags]]. if chunk == "#TAG:" then -- Tags are only case-insensitive with ASCII characters. local tag = (parser_extension_tags or get_parser_extension_tags())[lower(php_trim(pf_arg1))] if tag then pf_arg1 = format( "[[%s|%s]]", encode_uri(tag, "WIKI"), pf_arg1 ) end -- Otherwise, finalize pf_arg1 and add it to `chunks`. else pf_arg1 = finalize_arg(pf_arg1, (link_parameter_1 or get_link_parameter_1())[chunk]) end chunks[chunks_len + 1] = pf_arg1 -- Finalize pf_arg2 (if applicable), then return. if pf_arg2 then args = shallow_copy(args) -- Avoid destructively modifying args. args[1] = finalize_arg(pf_arg2, (link_parameter_2 or get_link_parameter_2())[chunk]) end return concat(chunks, "&#58;"), args -- : end function export.buildTemplate(title, args) local output = {title} if not args then return output end -- Iterate over all numbered parameters in order, followed by any -- remaining parameters in codepoint order. Implicit parameters are -- used wherever possible, even if explicit numbers are interpolated -- between them (e.g. 0 would go before any implicit parameters, and -- 2.5 between 2 and 3). -- TODO: handle "=" and "|" in params/values. local implicit for k, v in sorted_pairs(args) do if type(k) == "number" and k >= 1 and k % 1 == 0 then if implicit == nil then implicit = table_len(args) end insert(output, k <= implicit and v or k .. "=" .. v) else insert(output, k .. "=" .. v) end end return output end build_template = export.buildTemplate function export.templateLink(title, args, no_link) if not no_link then title, args = render_title(title, args) end local output = build_template(title, args) for i = 1, #output do output[i] = encode_entities(output[i], "={}", true, true) end return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&#123;&#123;" .. concat(output, "&#124;") .. "&#125;&#125;") -- {{ | }} ) end end do function export.find_parameters(text, not_transcluded) return parse(text, not not_transcluded):iterate_nodes("parameter") end function export.displayParameter(name, default) return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&#123;&#123;&#123;" .. concat({name, default}, "&#124;") .. "&#125;&#125;&#125;") -- {{{ | }}} ) end end do local function check_level(level) if type(level) ~= "number" then error("Heading levels must be numbers.") elseif level < 1 or level > 6 or level % 1 ~= 0 then error("Heading levels must be integers between 1 and 6.") end return level end -- FIXME: should headings which contain "\n" be returned? This may depend -- on variable factors, like template expansion. They iterate the heading -- count number, but fail on rendering. However, in some cases a different -- heading might still be rendered due to intermediate equals signs; it -- may even be of a different heading level: e.g., this is parsed as an -- L2 heading with a newline (due to the wikilink block), but renders as the -- L1 heading "=foo[[". Section edit links are sometimes (but not always) -- present in such cases. -- ==[[= -- ]]== -- TODO: section numbers for edit links seem to also include headings -- nested inside templates and parameters (but apparently not those in -- parser extension tags - need to test this more). If we ever want to add -- section edit links manually, this will need to be accounted for. function export.find_headings(text, i, j) local parsed = parse(text) if i == nil and j == nil then return parse(text):iterate_nodes("heading") end i = i and check_level(i) or 1 j = j and check_level(j) or 6 return parsed:iterate(function(v) if class_else_type(v) == "heading" then local level = v.level return level >= i and level <= j end end) end end do local function make_tag(tag) return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&lt;" .. tag .. "&gt;") ) end -- Note: invalid tags are returned without links. function export.wikitagLink(tag) -- ">" can't appear in tags (including attributes) since the parser -- unconditionally treats ">" as the end of a tag. if find(tag, ">", nil, true) then return make_tag(tag) end -- Tags must start "<tagname..." or "</tagname...", with no whitespace -- after "<" or "</". local slash, tagname, remainder = match(tag, "^(/?)([^/%s]+)(.*)$") if not tagname then return make_tag(tag) end -- Tags are only case-insensitive with ASCII characters. local link = lower(tagname) if ( -- onlyinclude tags must be lowercase and are whitespace intolerant. link == "onlyinclude" and (link ~= tagname or remainder ~= "") or -- Closing wikitags (except onlyinclude) can only have whitespace -- after the tag name. slash == "/" and not match(remainder, "^%s*()$") or -- Tagnames cannot be followed immediately by "/", unless it comes -- at the end (e.g. "<nowiki/>", but not "<nowiki/ >"). remainder ~= "/" and sub(remainder, 1, 1) == "/" ) then -- Output with no link. return make_tag(tag) end -- Partial transclusion tags aren't in the table of parser extension -- tags. if link == "noinclude" or link == "includeonly" or link == "onlyinclude" then link = "mw:Transclusion#Partial transclusion" else link = (parser_extension_tags or get_parser_extension_tags())[link] end if link then tag = gsub(tag, pattern_escape(tagname), "[[" .. replacement_escape(encode_uri(link, "WIKI")) .. "|%0]]", 1) end return make_tag(tag) end end -- For convenience. export.class_else_type = class_else_type return export g7883esvq9rqr796jeq8jsmk6y4r8t1 Module:parser 828 7957 54688 2026-09-25T16:43:15Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local scribunto_metamethods_module = "Module:Scribunto/metamethods" local table_deep_copy_module = "Module:table/deepCopy" local table_get_nested_module = "Module:table/getNested" local table_set_nested_module = "Module:table/setNested" local concat = table.concat local find = string.find local getmetatable = getmetatable local insert = table.insert local next = next local rawget = rawget local rawset = rawset local remove = table.remove local...' 54688 Scribunto text/plain local export = {} local scribunto_metamethods_module = "Module:Scribunto/metamethods" local table_deep_copy_module = "Module:table/deepCopy" local table_get_nested_module = "Module:table/getNested" local table_set_nested_module = "Module:table/setNested" local concat = table.concat local find = string.find local getmetatable = getmetatable local insert = table.insert local next = next local rawget = rawget local rawset = rawset local remove = table.remove local require = require local select = select local setmetatable = setmetatable local sub = string.sub local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local node_classes = {} local function deep_copy(...) deep_copy = require(table_deep_copy_module) return deep_copy(...) end local function get_nested(...) get_nested = require(table_get_nested_module) return get_nested(...) end local function set_nested(...) set_nested = require(table_set_nested_module) return set_nested(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local metamethods local function get_metamethods() -- Use require, since lookup times are much slower with mw.loadData. metamethods, get_metamethods = require(scribunto_metamethods_module), nil return metamethods end ------------------------------------------------------------------------------------ -- -- Helper functions -- ------------------------------------------------------------------------------------ local function inherit_metamethods(child, parent) if parent then for method, value in next, parent do if child[method] == nil and (metamethods or get_metamethods())[method] ~= nil then child[method] = value end end end return child end local function signed_index(t, n) return n and n <= 0 and #t + 1 + n or n end local function is_node(value) if value == nil then return false end local mt = getmetatable(value) return not (mt == nil or node_classes[mt] == nil) end local function node_class(value) if value == nil then return nil end local mt = getmetatable(value) if mt == nil then return nil end return mt ~= nil and node_classes[mt] or nil end local function class_else_type(value) if value == nil then return type(value) end local mt = getmetatable(value) if mt == nil then return type(value) end local class = node_classes[mt] return class == nil and type(value) or class end -- Recursively calling tostring() adds to the C stack (limit: 200), whereas -- calling __tostring metamethods directly does not. Occasionally relevant when -- dealing with very deep nesting. local tostring do local _tostring = _G.tostring function tostring(value) if is_node(value) then return value:__tostring(value) end return _tostring(value) end end ------------------------------------------------------------------------------------ -- -- Nodes -- ------------------------------------------------------------------------------------ local Node = {} Node.__index = Node function Node:next(i) i = i + 1 return self[i], self, i end --[==[ Implements recursive iteration over a node tree. By default, when a node is encountered (which may contain other nodes), it is returned on the first iteration, and then any child nodes are returned on each subsequent iteration; the same process is then followed if any of those children contain nodes themselves. Once a particular node has been fully traversed, the iterator then continues with any sibling nodes. The iterator will use the `next` method of each node to traverse it, which may differ depending on the node class. Each iteration returns three values: `value`, `node` and `key`. Together, these can be used to manipulate the node tree at any given point without needing to know the full structure. Note that when the input node is returned on the first iteration, `node` and `key` will be nil. The optional argument `test` can be used to limit the return values. This should be a function that returns a boolean value, where a return value of true means that the child will be returned by the iterator. If a node is not returned by the iterator, it will still be traversed, as it may contain children that should be returned. The method `iterate_nodes` is provided as a special instance of iterate which uses `is_node` as the test.]==] function Node:iterate(test) local node, k, n, nodes, keys, returned_self = self, 0, 0 -- Special case if `test` is `is_node`. local is_node_is_test = test == is_node return function() if not returned_self then returned_self = true if test == nil or test(self) then return self end end -- Get `v`, which is the value at the last-returned key of the current node; if `v` is a node, it will be iterated over (i.e. recursive iteration). By default, `v` will be the last-returned value, but looking it up here means that any modifications made to the node during the loop will be taken into account. This makes it possible to swap one node out for something else (e.g. another node), or to remove it entirely, without being locked into recursively iterating over the old node; instead, the new node (if any) will be iterated over. This means node trees can be modified on-the-fly during the course of a single loop. local v, node_check = node[k], true while true do -- If `v` is a node, memoize the current node and key, then iterate over it. if node_check and is_node(v) then -- `n` is the current memo level. n = n + 1 if nodes then nodes[n], keys[n] = node, k else nodes, keys = {node}, {k} end node, k = v, 0 end v, node, k = node:next(k) -- If `v` is nil, move down one level, then continue iterating the node on that level (if any), or otherwise terminate the loop. if v == nil then if n == 0 then return nil end node, k, n = nodes[n], keys[n], n - 1 elseif test == nil or test(v) then return v, node, k -- If `test` is `is_node`, there's no point checking it again on the next loop. elseif node_check and is_node_is_test then node_check = false end end end end function Node:iterate_nodes(...) local args_n = select("#", ...) if args_n == 0 then return self:iterate(is_node) elseif args_n == 1 then local class = ... return self:iterate(function(value) return node_class(value) == class end) end local classes = {} for i = 1, args_n do classes[select(i, ...)] = true end return self:iterate(function(value) return classes[node_class(value)] end) end function Node:__tostring() local output = {} for i = 1, #self do insert(output, tostring(self[i])) end return concat(output) end function Node:clone() return deep_copy(self, "keep", true) end function Node:new_class(class) local t = {type = class} t.__index = t t = inherit_metamethods(t, self) node_classes[t] = class return setmetatable(t, self) end function Node:new(t) rawset(t, "_parse_data", nil) return setmetatable(t, self) end do local Proxy = {} function Proxy:__index(k) local v = Proxy[k] if v ~= nil then return v end return self.__chars[k] end function Proxy:__newindex(k, v) local key = self.__keys[k] if key then self.__chars[k] = v self.__parents[key] = v elseif key == false then error("Character is immutable.") else error("Invalid key.") end end function Proxy:build(a, b, c) local len = self.__len + 1 self.__chars[len] = a self.__parents[len] = b self.__keys[len] = c self.__len = len end function Proxy:iter(i) i = i + 1 local char = self.__chars[i] if char ~= nil then return i, self[i], self, self.__parents[i], self.__keys[i] end end function Node:new_proxy() return setmetatable({ __node = self, __chars = {}, __parents = {}, __keys = {}, __len = 0 }, Proxy) end end function export.node() return Node:new_class("node") end ------------------------------------------------------------------------------------ -- -- Parser -- ------------------------------------------------------------------------------------ local Parser = {} Parser.__index = Parser function Parser:get_layer(n) if n ~= nil then return rawget(self, #self + n) end return self.current_layer end function Parser:emit(a, b) local layer = self.current_layer if b ~= nil then insert(layer, signed_index(layer, a), b) else rawset(layer, #layer + 1, a) end end function Parser:emit_tokens(a, b) local layer = self.current_layer if b ~= nil then a = signed_index(layer, a) for i = 1, #b do insert(layer, a + i - 1, b[i]) end else local len = #layer for i = 1, #a do len = len + 1 rawset(layer, len, a[i]) end end end function Parser:remove(n) local layer = self.current_layer if n ~= nil then return remove(layer, signed_index(layer, n)) end local len = #layer local token = layer[len] layer[len] = nil return token end function Parser:replace(a, b) local layer = self.current_layer layer[signed_index(layer, a)] = b end -- Unlike default table.concat, this respects __tostring metamethods. function Parser:concat(a, b, c) if a == nil or a > 0 then return self:concat(0, a, b) end local layer, ret, n = self:get_layer(a), {}, 0 for i = b and signed_index(layer, b) or 1, c and signed_index(layer, c) or #layer do n = n + 1 ret[n] = tostring(layer[i]) end return concat(ret) end function Parser:emitted(delta) if delta == nil then delta = -1 end local get_layer, i = self.get_layer, 0 while true do local layer = get_layer(self, i) if layer == nil then return nil end local layer_len = #layer if -delta <= layer_len then return rawget(layer, layer_len + delta + 1) end delta = delta + layer_len i = i - 1 end end function Parser:push(route) local layer = {_parse_data = { head = self.head, route = route, }} self[#self + 1] = layer self.current_layer = layer end function Parser:push_sublayer(handler, inherit) local pdata = { handler = handler, sublayer = true, } local sublayer = { _parse_data = pdata, } if inherit then local layer_parse_data = self.current_layer._parse_data setmetatable(pdata, inherit_metamethods({ __index = layer_parse_data, __newindex = layer_parse_data }, getmetatable(layer_parse_data))) end self[#self + 1] = sublayer self.current_layer = sublayer end function Parser:pop() local len, layer = #self while true do layer = self[len] self[len] = nil len = len - 1 if len == 0 then self.current_layer = self break elseif layer._parse_data.sublayer == nil then self.current_layer = self[len] break end self:emit_tokens(layer) end return setmetatable(layer, nil) end function Parser:pop_sublayer() local len, layer = #self, self.current_layer self[len] = nil self.current_layer = len == 1 and self or self[len - 1] return setmetatable(layer, nil) end function Parser:get(route, ...) self:push(route) local layer = route(self, ...) if layer == nil then layer = self:traverse() end return layer end function Parser:try(route, ...) local failed_routes = self.failed_routes if failed_routes ~= nil then local failed_layer = get_nested(failed_routes, route, self.head) if failed_layer ~= nil then return false, failed_layer end end local layer = self:get(route, ...) return not layer._parse_data.fail, layer end function Parser:fail_route() local layer = self:pop() local pdata = layer._parse_data pdata.fail = true local layer_head = pdata.head set_nested(self, layer, "failed_routes", pdata.route, layer_head) self.head = layer_head return layer end function Parser:traverse() local consume, advance = self.consume, self.advance while true do local layer = consume(self) if layer ~= nil then return layer end advance(self) end end -- Converts a handler into a switch table the first time it's called, which avoids creating unnecessary objects, and prevents any scoping issues caused by parser methods being assigned to table keys before they've been declared. -- false is used as the default key. do local Switch = {} function Switch:__call(parser, this) return (self[this] or self[false])(parser, this) end function Parser:switch(func, t) local pdata = self.current_layer._parse_data -- Point handler to the new switch table if the calling function is the current handler. if pdata.handler == func then pdata.handler = t end return setmetatable(t, Switch) end end -- Generate a new parser class object, which is used as the template for any parser objects. These should be customized with additional/modified methods as needed. function Parser:new_class() local t = {} t.__index = t return setmetatable(inherit_metamethods(t, self), self) end -- Generate a new parser object, which is used for a specific parse. function Parser:new(text) return setmetatable({ text = text, head = 1 }, self) end function Parser:parse(data) local parser = self:new(data.text) local success, tokens = parser:try(unpack(data.route)) if #parser > 0 then -- This shouldn't happen. error("Parser exited with non-empty stack.") elseif success then local node = data.node return true, node[1]:new(tokens, unpack(node, 2)), parser elseif data.allow_fail then return false, nil, parser end error("Parser exited with failed route.") end export.class_else_type = class_else_type export.is_node = is_node export.tostring = tostring local ArrayParser = Parser:new_class() function ArrayParser:read(delta) local v = self.text[self.head + (delta or 0)] return v == nil and "" or v end function ArrayParser:advance(n) self.head = self.head + (n == nil and 1 or n) end function ArrayParser:jump(head) self.head = head end function ArrayParser:consume(this, ...) if this == nil then this = self:read() end local pdata = self.current_layer._parse_data return pdata.handler(self, this, ...) end function export.array_parser() return ArrayParser:new_class() end local StringParser = Parser:new_class() function StringParser:read(i, j) local head, i = self.head, i or 0 return sub(self.text, head + i, head + (j or i)) end function StringParser:advance(n) self.head = self.head + (n or self.current_layer._parse_data.step or 1) end function StringParser:jump(head) local pdata = self.current_layer._parse_data self.head, pdata.next, pdata.next_len = head, nil, nil end -- If `ignore_nonmatch` is set, any non-match segment before the match will be ignored. function StringParser:set_pattern(pattern, ignore_nonmatch) local pdata = self.current_layer._parse_data pdata.pattern, pdata.next, pdata.next_len = "(" .. pattern .. ")", nil, nil if ignore_nonmatch then pdata.ignore_nonmatch = true end end function StringParser:consume() local pdata = self.current_layer._parse_data local this = pdata.next -- Use `next` and `next_len` from the previous iteration, if available. if this then pdata.step, pdata.next, pdata.next_len = pdata.next_len, nil, nil return pdata.handler(self, this) end local text, head, loc1, loc2 = self.text, self.head loc1, loc2, this = find(text, pdata.pattern, head) -- If there was no match, default to find(text, "$", head), with `this` as -- the empty string. if not loc1 then this, loc1 = "", #text + 1 loc2 = loc1 - 1 -- zero-length matches cause loc2 to be less than loc1 end -- If `this` is at `head`, consume it. if loc1 == head then pdata.step = loc2 - loc1 + 1 -- If `ignore_nonmatch` is set, ignore everything before `this`, then -- consume it. elseif pdata.ignore_nonmatch then self.head, pdata.step = loc1, loc2 - loc1 + 1 -- Otherwise, consume everything before `this`, and memoize the match and -- match length; the length is dealt with separately, as it could be 0 if -- `next` is an index (e.g. if the pattern is the empty string). else this, pdata.step, pdata.next, pdata.next_len = sub(text, head, loc1 - 1), loc1 - head, this, loc2 - loc1 + 1 end return pdata.handler(self, this) end function export.string_parser() return StringParser:new_class() end return export itvslgsv46c1ywldkt63xzu5krykaov Module:Scribunto 828 7958 54689 2026-09-25T16:44:11Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local math_module = "Module:math" local dump = mw.dumpObject local format = string.format local gsub = string.gsub local match = string.match local php_trim -- defined below local sub = string.sub local tonumber = tonumber local tostring = tostring local type = type do local php_htmlspecialchars_data local function get_php_htmlspecialchars_data() php_htmlspecialchars_data, get_php_htmlspecialchars_data = { ["\""] = "&quot;", ["&"] =...' 54689 Scribunto text/plain local export = {} local math_module = "Module:math" local dump = mw.dumpObject local format = string.format local gsub = string.gsub local match = string.match local php_trim -- defined below local sub = string.sub local tonumber = tonumber local tostring = tostring local type = type do local php_htmlspecialchars_data local function get_php_htmlspecialchars_data() php_htmlspecialchars_data, get_php_htmlspecialchars_data = { ["\""] = "&quot;", ["&"] = "&amp;", ["'"] = "&#039;", ["<"] = "&lt;", [">"] = "&gt;", }, nil return php_htmlspecialchars_data end --[==[Lua equivalent of PHP's {{code|php|htmlspecialchars($string)}}, which converts the characters `&"'<>` to HTML entities. If the `quotes` flag is set to {"compat"}, then `'` will not be converted, and if it is set to {"noquotes"}, then neither `"` nor `'` will be converted.]==] function export.php_htmlspecialchars(str, quotes) if quotes == nil or quotes == "quotes" then quotes = "'\"" elseif quotes == "compat" then quotes = "\"" elseif quotes == "noquotes" then quotes = "" else local quotes_type = type(quotes) error('`quotes` must be "quotes", "compat", "noquotes" or nil; received ' .. (quotes_type == "string" and dump(quotes) or "a " .. quotes_type)) end return (gsub(str, "[&<>" .. quotes .. "]", php_htmlspecialchars_data or get_php_htmlspecialchars_data())) end end do local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end -- Normalizes a string for use in comparisons which emulate PHP's equals -- operator, which coerces certain strings to numbers: those within the -- range -2^63 to 2^63 - 1 which don't have decimal points or exponents are -- coerced to integers, while any others are coerced to doubles if possible; -- otherwise, they remain as strings. PHP and Lua have the same precision -- for doubles, but Lua's integer precision range is -2^53 + 1 to 2^53 - 1. -- Any integers within Lua's precision, as well as all doubles, are simply -- coerced to numbers, but PHP integers outside of Lua's precision are -- emulated as normalized strings, with leading 0s and any + sign removed. -- The `not_long` flag is used for the second comparator if the first did -- not get normalized to a long integer, as PHP will only coerce strings to -- integers if it's possible for both comparators. local function php_normalize_string(str, not_long) local num = tonumber_extended(str, nil, true) -- Must be a number that isn't ±infinity, NaN or hexadecimal. if not num or match(str, "^%s*[+-]?0[xX]()") then return str -- If `not_long` is set or `num` is within Lua's precision, return as a -- number. elseif not_long or num < 9007199254740992 and num > -9007199254740992 then return num, "number" end -- Check if it could be a long integer, and return as a double if not. local sign, str_no_0 = match(str, "^%s*([+-]?)0*(%d+)$") if not str_no_0 then return num, "number" end -- Otherwise, check if it's a long integer. 2^63 is 9223372036854775808, -- so slice off the last 15 digits and deal with the two parts -- separately. If the integer value would be too high/low, return as a -- string. local high = tonumber(sub(str_no_0, 1, -16)) if high > 9223 then return str elseif high == 9223 then local low = tonumber(sub(str_no_0, -15)) -- Range is -2^63 to 2^63 - 1 (not symmetrical). if low > 372036854775808 or low == 372036854775808 and sign ~= "-" then return str end end return (sign == "+" and "" or sign) .. str_no_0, "long integer", num end --[==[Lua equivalent of PHP's {{code|php|===}} operator for strings.]==] function export.php_string_equals(str1, str2) if str1 == str2 then return true end local str1, str1_type, str1_num = php_normalize_string(str1) if str1 == str2 then return true elseif str1_type == "long integer" then local str2, str2_type = php_normalize_string(str2) return str2 == (str2_type == "number" and str1_num or str1) elseif str1_type == "number" then return str1 == php_normalize_string(str2, true) end return false end end --[==[Lua equivalent of PHP's {{code|php|trim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "}. This is useful when dealing with template parameters, since the native parser trims them like this.]==] function export.php_trim(str) return match(str, "[^ \t-\v\r%z].*%f[ \t-\v\r%z]") or "" end php_trim = export.php_trim --[==[Lua equivalent of PHP's {{code|php|ltrim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "} from the beginning of the input string.]==] function export.php_ltrim(str) return (gsub(str, "^[ \t-\v\r%z]+", "")) end --[==[Lua equivalent of PHP's {{code|php|rtrim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "} from the end of the input string.]==] function export.php_rtrim(str) return match(str, "^.+%f[ \t-\v\r%z]") or "" end --[==[Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a {frame.args} table). For example, {"1"} (a string) is normalized to {1} (a number), {" foo "} is normalized to {"foo"}, and {1.5} (a number) is normalized to {"1.5"} (a string). Inputs which cannot be normalized (e.g. booleans) return {nil}. Strings are trimmed with {export.php_trim}, unless the `no_trim` flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling {frame:expandTemplates()}). After trimming (if applicable), keys are then converted to numbers if '''all''' of the following are true: # They are integers; i.e. no decimals or leading zeroes (e.g. {"2"}, but not {"2.0"} or {"02"}). # They are ≤ 2{{sup|53}} and ≥ -2{{sup|53}}. # There is no leading sign unless < 0 (e.g. {"2"} or {"-2"}, but not {"+2"} or {"-0"}). # They contain no leading or trailing whitespace (which may be present when the `no_trim` flag is set). Numbers are converted to strings if '''either''': # They are not integers (e.g. {1.5}). # They are > 2{{sup|53}} or < -2{{sup|53}}. When converted to strings, integers ≤ 2{{sup|63}} and ≥ -2{{sup|63}} are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 2{{sup|53}} to < -2{{sup|53}}. All other numbers use the standard formatting output by {tostring()}.]==] function export.scribunto_parameter_key(key, no_trim) local key_type = type(key) if key_type == "string" then if not no_trim then key = php_trim(key) end if match(key, "^()-?[1-9]%d*$") then local num = tonumber(key) -- Lua integers are only precise to 2^53 - 1, so specifically check -- for 2^53 and -2^53 as strings, since a numerical comparison won't -- work as it can't distinguish 2^53 from 2^53 + 1. return ( num <= 9007199254740991 and num >= -9007199254740991 or key == "9007199254740992" or key == "-9007199254740992" ) and num or key end return key == "0" and 0 or key elseif key_type == "number" then -- No special handling needed for inf or NaN. return key % 1 == 0 and ( key <= 9007199254740992 and key >= -9007199254740992 and key or key <= 9223372036854775808 and key >= -9223372036854775808 and format("%d", key) ) or tostring(key) end return nil end --[==[Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a {frame.args} table), which is always a string. For example, {"foo"} remains the same, {2} (a number) is normalized to {"2"} (a string), {true} is normalized to {"1"}, and {false} is normalized to {""}. Inputs which cannot be normalized (e.g. tables) return {nil}. By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. `bar` in {{tl|<nowiki/>foo|bar}}). If the `named` flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. `baz` in {{tl|<nowiki/>foo|bar=baz}}).]==] function export.scribunto_parameter_value(value, named) local value_type = type(value) if value_type == "string" then return named and php_trim(value) or value elseif value_type == "number" then return tostring(value) elseif value_type == "boolean" then return value and "1" or "" end return nil end return export 90dvo9iqmpdordtuzghbnnl9rw4476r Module:Scribunto/metamethods 828 7959 54690 2026-09-25T16:44:41Z Deadend0914 7211 Gesceop tramet þe hafaþ 'return { __add = true, __call = true, __concat = true, __div = true, __eq = true, __gc = true, __index = true, __ipairs = true, __le = true, __len = true, __lt = true, __metatable = true, __mod = true, __mode = true, __mul = true, __newindex = true, __pairs = true, __pow = true, __sub = true, __tostring = true, __unm = true, }' 54690 Scribunto text/plain return { __add = true, __call = true, __concat = true, __div = true, __eq = true, __gc = true, __index = true, __ipairs = true, __le = true, __len = true, __lt = true, __metatable = true, __mod = true, __mode = true, __mul = true, __newindex = true, __pairs = true, __pow = true, __sub = true, __tostring = true, __unm = true, } 9ldqvu9rhlfkuksyro1b52m6ychbevm Module:load 828 7960 54691 2026-09-25T16:45:17Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local load_data = mw.loadData local loaded = package.loaded local main_loader = package.loaders[2] local require = require local setmetatable = setmetatable local loaders, loaded_data, mt local function get_mt() mt, get_mt = {__mode = "kv"}, nil return mt end -- main_loader returns a loader function if the module exists, or nil if it doesn't. local function get_loader(modname) if loaders == nil then local loader = main_loader(modname)...' 54691 Scribunto text/plain local export = {} local load_data = mw.loadData local loaded = package.loaded local main_loader = package.loaders[2] local require = require local setmetatable = setmetatable local loaders, loaded_data, mt local function get_mt() mt, get_mt = {__mode = "kv"}, nil return mt end -- main_loader returns a loader function if the module exists, or nil if it doesn't. local function get_loader(modname) if loaders == nil then local loader = main_loader(modname) loaders = setmetatable({[modname] = loader or false}, mt or get_mt()) return loader end local loader = loaders[modname] if loader == nil then loader = main_loader(modname) loaders[modname] = loader or false end return loader or nil end local function get_data(modname, loader) if loaded_data == nil then local data = load_data(modname) loaded_data = setmetatable({[loader] = data}, mt or get_mt()) return data end local data = loaded_data[loader] if data == nil then data = load_data(modname) loaded_data[loader] = data end return data end --[==[ Like `require`, but returns `nil` if a module does not exist, instead of throwing an error. Outputs are cached, which is faster for all modules, but much faster for nonexistent modules, since `require` will attempt to use the full loader each time because loading failures don't get cached.]==] function export.safe_require(modname) local mod = loaded[modname] if mod == nil and get_loader(modname) then -- Call with require instead of the loader directly, as it protects against infinite loading loops. return require(modname) end return mod end --[==[ Like `mw.loadData`, but it does not generate a new table each time the same module is loaded via this function. Instead, it caches the returned data so that the same table can be returned each time, which is more efficient.]==] function export.load_data(modname) return get_data(modname, get_loader(modname)) end --[==[ Like `export.load_data` (itself a variant of `mw.loadData`), but returns `nil` if a module does not exist, instead of throwing an error. Outputs are cached, which is faster for all modules, but much faster for nonexistent modules, since `mw.loadData` will attempt to use the full loader each time because loading failures don't get cached.]==] function export.safe_load_data(modname) local loader = get_loader(modname) if loader == nil then return nil end return get_data(modname, loader) end return export ane0ji5isx9pytu6xaiacn5a70oo2t2 Module:template parser/data 828 7961 54692 2026-09-25T16:45:55Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local string = string local case_insensitive_pattern = require("Module:string utilities").case_insensitive_pattern local gsub = string.gsub local upper = string.upper local data = {} do local tags = mw.loadData("Module:data/parser extension tags") local data_end_tags = {} -- Generates the string pattern for the end tag. -- The preprocessor uses the regex "/<\/TAG\s*>/i", so only ASCII characters -- are case-insensitive. local function end_tag_pattern(t...' 54692 Scribunto text/plain local string = string local case_insensitive_pattern = require("Module:string utilities").case_insensitive_pattern local gsub = string.gsub local upper = string.upper local data = {} do local tags = mw.loadData("Module:data/parser extension tags") local data_end_tags = {} -- Generates the string pattern for the end tag. -- The preprocessor uses the regex "/<\/TAG\s*>/i", so only ASCII characters -- are case-insensitive. local function end_tag_pattern(tag) data_end_tags[tag] = "</" .. case_insensitive_pattern(tag, nil, true) .. "%s*>" end for tag in pairs(tags) do end_tag_pattern(tag) end end_tag_pattern("includeonly") end_tag_pattern("noinclude") data_end_tags["onlyinclude"] = true -- Pattern is not required, but a key is needed for tag validity checks. data.end_tags = data_end_tags end -- The parser's HTML sanitizer validates tag attributes with the regex -- "/^([:_\p{L}\p{N}][:_\.\-\p{L}\p{N}]*)$/sxu". Ustring's "%w" is defined as -- "[\p{L}\p{Nd}]", so any characters in \p{N} but not \p{Nd} must be added -- manually. -- NOTE: \p{N} *MUST* be defined according to the same version of Unicode that -- the sanitizer uses in order to remain in sync. As of September 2024, this is -- version 11.0. local N_not_Nd = "\194\178" .. -- U+00B2 "\194\179" .. -- U+00B3 "\194\185" .. -- U+00B9 "\194\188-\194\190" .. -- U+00BC-U+00BE "\224\167\180-\224\167\185" .. -- U+09F4-U+09F9 "\224\173\178-\224\173\183" .. -- U+0B72-U+0B77 "\224\175\176-\224\175\178" .. -- U+0BF0-U+0BF2 "\224\177\184-\224\177\190" .. -- U+0C78-U+0C7E "\224\181\152-\224\181\158" .. -- U+0D58-U+0D5E "\224\181\176-\224\181\184" .. -- U+0D70-U+0D78 "\224\188\170-\224\188\179" .. -- U+0F2A-U+0F33 "\225\141\169-\225\141\188" .. -- U+1369-U+137C "\225\155\174-\225\155\176" .. -- U+16EE-U+16F0 "\225\159\176-\225\159\185" .. -- U+17F0-U+17F9 "\225\167\154" .. -- U+19DA "\226\129\176" .. -- U+2070 "\226\129\180-\226\129\185" .. -- U+2074-U+2079 "\226\130\128-\226\130\137" .. -- U+2080-U+2089 "\226\133\144-\226\134\130" .. -- U+2150-U+2182 "\226\134\133-\226\134\137" .. -- U+2185-U+2189 "\226\145\160-\226\146\155" .. -- U+2460-U+249B "\226\147\170-\226\147\191" .. -- U+24EA-U+24FF "\226\157\182-\226\158\147" .. -- U+2776-U+2793 "\226\179\189" .. -- U+2CFD "\227\128\135" .. -- U+3007 "\227\128\161-\227\128\169" .. -- U+3021-U+3029 "\227\128\184-\227\128\186" .. -- U+3038-U+303A "\227\134\146-\227\134\149" .. -- U+3192-U+3195 "\227\136\160-\227\136\169" .. -- U+3220-U+3229 "\227\137\136-\227\137\143" .. -- U+3248-U+324F "\227\137\145-\227\137\159" .. -- U+3251-U+325F "\227\138\128-\227\138\137" .. -- U+3280-U+3289 "\227\138\177-\227\138\191" .. -- U+32B1-U+32BF "\234\155\166-\234\155\175" .. -- U+A6E6-U+A6EF "\234\160\176-\234\160\181" .. -- U+A830-U+A835 "\240\144\132\135-\240\144\132\179" .. -- U+10107-U+10133 "\240\144\133\128-\240\144\133\184" .. -- U+10140-U+10178 "\240\144\134\138" .. -- U+1018A "\240\144\134\139" .. -- U+1018B "\240\144\139\161-\240\144\139\187" .. -- U+102E1-U+102FB "\240\144\140\160-\240\144\140\163" .. -- U+10320-U+10323 "\240\144\141\129" .. -- U+10341 "\240\144\141\138" .. -- U+1034A "\240\144\143\145-\240\144\143\149" .. -- U+103D1-U+103D5 "\240\144\161\152-\240\144\161\159" .. -- U+10858-U+1085F "\240\144\161\185-\240\144\161\191" .. -- U+10879-U+1087F "\240\144\162\167-\240\144\162\175" .. -- U+108A7-U+108AF "\240\144\163\187-\240\144\163\191" .. -- U+108FB-U+108FF "\240\144\164\150-\240\144\164\155" .. -- U+10916-U+1091B "\240\144\166\188" .. -- U+109BC "\240\144\166\189" .. -- U+109BD "\240\144\167\128-\240\144\167\143" .. -- U+109C0-U+109CF "\240\144\167\146-\240\144\167\191" .. -- U+109D2-U+109FF "\240\144\169\128-\240\144\169\136" .. -- U+10A40-U+10A48 "\240\144\169\189" .. -- U+10A7D "\240\144\169\190" .. -- U+10A7E "\240\144\170\157-\240\144\170\159" .. -- U+10A9D-U+10A9F "\240\144\171\171-\240\144\171\175" .. -- U+10AEB-U+10AEF "\240\144\173\152-\240\144\173\159" .. -- U+10B58-U+10B5F "\240\144\173\184-\240\144\173\191" .. -- U+10B78-U+10B7F "\240\144\174\169-\240\144\174\175" .. -- U+10BA9-U+10BAF "\240\144\179\186-\240\144\179\191" .. -- U+10CFA-U+10CFF "\240\144\185\160-\240\144\185\190" .. -- U+10E60-U+10E7E "\240\144\188\157-\240\144\188\166" .. -- U+10F1D-U+10F26 "\240\144\189\145-\240\144\189\148" .. -- U+10F51-U+10F54 "\240\145\129\146-\240\145\129\165" .. -- U+11052-U+11065 "\240\145\135\161-\240\145\135\180" .. -- U+111E1-U+111F4 "\240\145\156\186" .. -- U+1173A "\240\145\156\187" .. -- U+1173B "\240\145\163\170-\240\145\163\178" .. -- U+118EA-U+118F2 "\240\145\177\154-\240\145\177\172" .. -- U+11C5A-U+11C6C "\240\146\144\128-\240\146\145\174" .. -- U+12400-U+1246E "\240\150\173\155-\240\150\173\161" .. -- U+16B5B-U+16B61 "\240\150\186\128-\240\150\186\150" .. -- U+16E80-U+16E96 "\240\157\139\160-\240\157\139\179" .. -- U+1D2E0-U+1D2F3 "\240\157\141\160-\240\157\141\184" .. -- U+1D360-U+1D378 "\240\158\163\135-\240\158\163\143" .. -- U+1E8C7-U+1E8CF "\240\158\177\177-\240\158\178\171" .. -- U+1EC71-U+1ECAB "\240\158\178\173-\240\158\178\175" .. -- U+1ECAD-U+1ECAF "\240\158\178\177-\240\158\178\180" .. -- U+1ECB1-U+1ECB4 "\240\159\132\128-\240\159\132\140" -- U+1F100-U+1F10C data.valid_attribute_name = "^[:_%w" .. N_not_Nd .."][:_.%-%w" .. N_not_Nd .. "]*$" -- Value is the namespace number of the linked page at parameter 0, where 0 is mainspace. -- If the namespace is the mainspace, it can be overridden by an explicitly specified category (e.g. {{PAGENAME:Category:Foo}} refers to "Category:Foo"). This does not apply to any other namespace (e.g. {{#SPECIAL:Category:Foo}} refers to "Special:Category:Foo"). data.template_link_param_1 = { ["#CATEGORYTREE:"] = 14, -- Category: ["#IFEXIST:"] = 0, ["#INVOKE:"] = 828, -- Module: ["#LST:"] = 0, ["#LSTH:"] = 0, ["#LSTX:"] = 0, ["#SPECIAL:"] = -1, -- Special: ["#SPECIALE:"] = -1, -- Special: ["#TITLEPARTS:"] = 0, ["BASEPAGENAME:"] = 0, ["BASEPAGENAMEE:"] = 0, ["CANONICALURL:"] = 0, ["CANONICALURLE:"] = 0, ["CASCADINGSOURCES:"] = 0, ["FILEPATH:"] = 6, -- File: ["FULLPAGENAME:"] = 0, ["FULLPAGENAMEE:"] = 0, ["FULLURL:"] = 0, ["FULLURLE:"] = 0, ["INT:"] = 8, -- MediaWiki: ["LOCALURL:"] = 0, ["LOCALURLE:"] = 0, ["NAMESPACE:"] = 0, ["NAMESPACEE:"] = 0, ["NAMESPACENUMBER:"] = 0, ["PAGEID:"] = 0, ["PAGENAME:"] = 0, ["PAGENAMEE:"] = 0, ["PAGESINCATEGORY:"] = 14, -- Category: ["PAGESIZE:"] = 0, ["REVISIONDAY:"] = 0, ["REVISIONDAY2:"] = 0, ["REVISIONID:"] = 0, ["REVISIONMONTH:"] = 0, ["REVISIONMONTH1:"] = 0, ["REVISIONTIMESTAMP:"] = 0, ["REVISIONUSER:"] = 0, ["REVISIONYEAR:"] = 0, ["ROOTPAGENAME:"] = 0, ["ROOTPAGENAMEE:"] = 0, ["SUBJECTPAGENAME:"] = 0, ["SUBJECTPAGENAMEE:"] = 0, ["SUBJECTSPACE:"] = 0, ["SUBJECTSPACEE:"] = 0, ["SUBPAGENAME:"] = 0, ["SUBPAGENAMEE:"] = 0, ["TALKPAGENAME:"] = 0, ["TALKPAGENAMEE:"] = 0, ["TALKSPACE:"] = 0, ["TALKSPACEE:"] = 0, } -- Value is the namespace number of the linked page at parameter 1. data.template_link_param_2 = { ["PROTECTIONEXPIRY:"] = 0, ["PROTECTIONLEVEL:"] = 0, } return data t4rorng7wydircsbpm0pxhr9wjhuh7e Module:data/parser extension tags 828 7962 54693 2026-09-25T16:46:40Z Deadend0914 7211 Gesceop tramet þe hafaþ '--Note: noinclude, includeonly and onlyinclude are not parser extension tags, as they are handled in a special way by the preprocessor. Among other things, they cannot be used with {{#TAG:}}, for example. return { ["categorytree"] = "mw:Extension:CategoryTree", ["ce"] = "mw:Extension:Math", ["charinsert"] = "mw:Extension:CharInsert", ["chem"] = "mw:Extension:Math", ["dynamicpagelist"] = "mw:Extension:DynamicPageList (Wikimedia)", ["gallery"] = "mw:Help:Ima...' 54693 Scribunto text/plain --Note: noinclude, includeonly and onlyinclude are not parser extension tags, as they are handled in a special way by the preprocessor. Among other things, they cannot be used with {{#TAG:}}, for example. return { ["categorytree"] = "mw:Extension:CategoryTree", ["ce"] = "mw:Extension:Math", ["charinsert"] = "mw:Extension:CharInsert", ["chem"] = "mw:Extension:Math", ["dynamicpagelist"] = "mw:Extension:DynamicPageList (Wikimedia)", ["gallery"] = "mw:Help:Images#Rendering a gallery of images", ["graph"] = "mw:Extension:Graph", ["hiero"] = "mw:Extension:WikiHiero", ["imagemap"] = "mw:Extension:ImageMap", ["indicator"] = "mw:Help:Page status indicators#Adding page status indicators", ["inputbox"] = "mw:Extension:InputBox", ["langconvert"] = "mw:Parser extension tags", -- undocumented ["mapframe"] = "mw:Extension:Kartographer", ["maplink"] = "mw:Extension:Kartographer", ["math"] = "mw:Extension:Math", ["nowiki"] = "mw:Help:Formatting", ["phonos"] = "mw:Extension:Phonos", ["poem"] = "mw:Extension:Poem", ["pre"] = "mw:Help:Formatting", ["ref"] = "mw:Extension:Cite", ["references"] = "mw:Extension:Cite", ["score"] = "mw:Extension:Score", ["section"] = "mw:Extension:Labeled Section Transclusion", ["source"] = "mw:Extension:SyntaxHighlight", ["syntaxhighlight"] = "mw:Extension:SyntaxHighlight", ["talkpage"] = "mw:Extension:LiquidThreads", -- undocumented ["templatedata"] = "mw:Extension:TemplateData", ["templatestyles"] = "mw:Extension:TemplateStyles", ["thread"] = "mw:Extension:LiquidThreads", -- undocumented ["timeline"] = "mw:Extension:EasyTimeline", } ofvfawk3x15dsa9oos9tt1283gyzpwv Module:memoize 828 7963 54695 2026-09-25T16:49:22Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local math_module = "Module:math" local table_pack_module = "Module:table/pack" local require = require local select = select local unpack = unpack or table.unpack -- Lua 5.2 compatibility -- table.pack: in Lua 5.2+, this is a function that wraps the parameters given -- into a table with the additional key `n` that contains the total number of -- parameters given. This is not available on Lua 5.1, so [[Module:table/pack]] -- provides the same functionality. loc...' 54695 Scribunto text/plain local math_module = "Module:math" local table_pack_module = "Module:table/pack" local require = require local select = select local unpack = unpack or table.unpack -- Lua 5.2 compatibility -- table.pack: in Lua 5.2+, this is a function that wraps the parameters given -- into a table with the additional key `n` that contains the total number of -- parameters given. This is not available on Lua 5.1, so [[Module:table/pack]] -- provides the same functionality. local function pack(...) pack = require(table_pack_module) return pack(...) end local function sign(...) sign = require(math_module).sign return sign(...) end ----- M E M O I Z A T I O N----- -- Memoizes a function or callable table. -- Supports any number of arguments and return values. -- If the optional parameter `simple` is set, then the memoizer will use a faster implementation, but this is only compatible with one argument and one return value. If `simple` is set, additional arguments will be accepted, but this should only be done if those arguments will always be the same. -- Sentinels. local _nil, neg_0, pos_nan, neg_nan = {}, {}, {}, {} -- Certain values can't be used as table keys, so they require sentinels as well: e.g. f("foo", nil, "bar") would be memoized at memo["foo"][_nil]["bar"][memo]. These values are: -- nil. -- -0, which is equivalent to 0 in most situations, but becomes "-0" on conversion to string; it also behaves differently in some operations (e.g. 1/a evaluates to inf if a is 0, but -inf if a is -0). -- NaN and -NaN, which are the only values for which n == n is false; they only seem to differ on conversion to string ("nan" and "-nan"). local function get_key(x) if x == x then return x == nil and _nil or x == 0 and 1 / x < 0 and neg_0 or x end return sign(x) == 1 and pos_nan or neg_nan end -- Return values are memoized as tables of return values, which are looked up using each input argument as a key, followed by `memo`. e.g. if the input arguments were (1, 2, 3), the memo would be located at t[1][2][3][memo]. `memo` is always used as the final lookup key so that (for example) the memo for f(1, 2, 3), f[1][2][3][memo], doesn't interfere with the memo for f(1, 2), f[1][2][memo]. local function get_memo(memo, n, nargs, key, ...) key = get_key(key) local next_memo = memo[key] if next_memo == nil then next_memo = {} memo[key] = next_memo end memo = next_memo return n == nargs and memo or get_memo(memo, n + 1, nargs, ...) end -- Used to catch the function output values instead of using a table directly, -- since pack() returns a table with the key `n`, giving the number of return -- values, even if they are nil. This ensures that any nil return values after -- the last non-nil value will always be present (e.g. pack() gives {n = 0}, -- pack(nil) gives {n = 1}, pack(nil, "foo", nil) gives {[2] = "foo", n = 3} -- etc.). The distinction between nil and nothing affects some native functions -- (e.g. tostring() throws an error, but tostring(nil) returns "nil"), so it -- needs to be reconstructable from the memo. local function memoize_then_return(memo, _memo, ...) _memo[memo] = pack(...) return ... end return function(func, simple) local memo = {} if simple then return function(...) local key = get_key((...)) local output = memo[key] if output == nil then output = func(...) memo[key] = output == nil and _nil or output return output elseif output == _nil then return nil end return output end end return function(...) local nargs = select("#", ...) -- Since all possible inputs need to be memoized (including true, false -- and nil), the memo table itself is used as a sentinel to ensure that -- the table of arguments will always have a unique key. local _memo = nargs == 0 and memo or get_memo(memo, 1, nargs, ...) local output = _memo[memo] -- If get_memo() returned nil, call `func` with the arguments and catch -- the output with memoize_then_return(); this packs the return values -- into a table to memoize them, then returns them. Since the return -- values are available to it as `...`, this avoids the need to call -- unpack() on the memoized table on the first call, as they can be -- returned directly. if output == nil then return memoize_then_return(memo, _memo, func(...)) end -- Unpack from 1 to the original number of return values (memoized at -- key `n`); unpack() returns nil for any values not in output. return unpack(output, 1, output.n) end end 8gmyvbgmtmwh2pu7neciscs7liw1v30 Bysen:ang-decl-noun 10 7964 54697 2026-09-25T16:54:13Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:ang-noun|make_table}}<!--' 54697 wikitext text/x-wiki {{#invoke:ang-noun|make_table}}<!-- for4oh5z14vo5qi3zjo7t7qmljnmyaw 54713 54697 2026-09-25T17:54:59Z Deadend0914 7211 54713 wikitext text/x-wiki {{#invoke:ang-noun|make_table}}<!-- --><noinclude>{{documentation}}</noinclude> hs3z8byefav9f7spfhd975yslulzz9v Module:inflection-table 828 7965 54698 2026-09-25T16:55:01Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local M = require("Module:module loader").init({ require = { parameters = "Module:parameters", }, loadData = { headword_data = "Module:headword/data", }, }) local concat = table.concat local insert = table.insert local WRAPPER_CLASS = "inflection-table-wrapper" local TABLE_CLASS = "inflection-table" local TITLE_CLASS = "inflection-table-title" local NO_TITLE_CLASS = "inflection-table-no-title" local NOTES_CLASS = "inflection-table-not...' 54698 Scribunto text/plain local export = {} local M = require("Module:module loader").init({ require = { parameters = "Module:parameters", }, loadData = { headword_data = "Module:headword/data", }, }) local concat = table.concat local insert = table.insert local WRAPPER_CLASS = "inflection-table-wrapper" local TABLE_CLASS = "inflection-table" local TITLE_CLASS = "inflection-table-title" local NO_TITLE_CLASS = "inflection-table-no-title" local NOTES_CLASS = "inflection-table-notes" local COLLAPSIBLE_CLASSES = "inflection-table-collapsible inflection-table-collapsed no-vc" local VS_SWITCHER_CLASS = "vsSwitcher" local VS_TOGGLE_CLASS = "vsToggleElement" local BOX_CLASS = "inflection-box" local BOX_CONTENT_CLASS = "inflection-box-content" local BOX_NARROW_CLASS = "inflection-table-narrow" local TR_ALONGSIDE_CLASS = "tr-alongside" local FLOW_VERTICAL_CLASS = "flow-vertical" local NO_TITLE_MARKER = "-" local DEFAULT_PALETTE = "grey" local DEFAULT_TOGGLE_CATEGORY = "inflection" local DEFAULT_TITLE_FORMAT = "Inflection of ''%s''" local VERTICAL_FLOW = "vertical" local BASE_STYLE = "width: fit-content" -- Palettes recognized by [[MediaWiki:Gadget-Palette.css]]. local PALETTES = { red = true, scarlet = true, orange = true, amber = true, yellow = true, lime = true, green = true, teal = true, cyan = true, blue = true, indigo = true, purple = true, magenta = true, rose = true, grey = true, brown = true, } local top_params = { title = true, tall = true, class = true, ["min-width"] = true, palette = true, category = true, lang = true, ["vs-category"] = true, vsToggleElement = true, } local bottom_params = { notes = true, } local box_top_params = { title = true, flow = true, category = true, } local function nonempty(value) return value ~= nil and value ~= "" and value or nil end -- Builds the opening <div> and the wikitable header. The markup is deliberately -- unbalanced -- {{inflection-table-bottom}} closes both -- so this is assembled -- as text rather than through mw.html, which cannot emit an unclosed node. function export.make_top(args) local palette = args.palette or DEFAULT_PALETTE if not PALETTES[palette] then error(("Unrecognized palette '%s'; see [[MediaWiki:Gadget-Palette.css]] for the recognized palettes.") :format(palette)) end local no_title = args.title == NO_TITLE_MARKER local wrapper_classes = { WRAPPER_CLASS } if no_title then insert(wrapper_classes, NO_TITLE_CLASS) end if nonempty(args.tall) then insert(wrapper_classes, COLLAPSIBLE_CLASSES) end if nonempty(args.class) then insert(wrapper_classes, args.class) end local style = BASE_STYLE local min_width = nonempty(args["min-width"]) if min_width then style = ("%s; min-width: %s"):format(style, min_width) end local vs_category = nonempty(args["vs-category"]) local table_classes = { TABLE_CLASS } if nonempty(args.lang) then insert(table_classes, ("%s-%s"):format(TABLE_CLASS, args.lang)) end if vs_category then insert(table_classes, VS_SWITCHER_CLASS) end local lines = { ('<div class="%s" style="%s" data-wikt-palette-p="%s" data-toggle-category="%s">'):format( concat(wrapper_classes, " "), style, palette, args.category or DEFAULT_TOGGLE_CATEGORY ), ('{| class="%s"%s'):format( concat(table_classes, " "), vs_category and (' data-toggle-category="%s"'):format(vs_category) or "" ), } if not no_title then insert(lines, ('|+ class="%s%s" | %s'):format( TITLE_CLASS, nonempty(args.vsToggleElement) and " " .. VS_TOGGLE_CLASS or "", args.title or DEFAULT_TITLE_FORMAT:format(M.headword_data.pagename) )) insert(lines, "|-") end return concat(lines, "\n") .. "\n" end -- Builds the opening markup for {{inflection-box-top}}, a wrapper holding -- several complete inflection tables side by side. Boxes always place -- transliterations alongside, so that class is fixed. No palette is emitted: -- each table inside the box carries its own. -- {{inflection-table-bottom}} closes this too. function export.make_box_top(args) local no_title = args.title == NO_TITLE_MARKER local classes = { WRAPPER_CLASS, BOX_CLASS, BOX_NARROW_CLASS, } if no_title then insert(classes, NO_TITLE_CLASS) -- zeroes the wrapper's border and padding else insert(classes, COLLAPSIBLE_CLASSES) end insert(classes, TR_ALONGSIDE_CLASS) if args.flow == VERTICAL_FLOW then insert(classes, FLOW_VERTICAL_CLASS) end local style, table_style = BASE_STYLE, "" if no_title then style = style .. "; background: none" table_style = ' style="background: none"' end local lines = { ('<div class="%s" style="%s" data-toggle-category="%s">'):format( concat(classes, " "), style, args.category or DEFAULT_TOGGLE_CATEGORY ), ('{| class="%s"%s'):format(BOX_CONTENT_CLASS, table_style), } if not no_title then insert(lines, ('|+ class="%s" | %s'):format( TITLE_CLASS, args.title or DEFAULT_TITLE_FORMAT:format(M.headword_data.pagename) )) end insert(lines, "|-") insert(lines, "|") return concat(lines, "\n") .. "\n" end -- Closes the wikitable and the wrapper opened by make_top, with the notes block -- in between. function export.make_bottom(args) local notes = args and nonempty(args.notes) if not notes then -- The blank line is what the template emitted; kept so the parser sees -- byte-identical wikitext. return "|}\n\n</div>" end return ('|}\n<div class="%s">\n%s\n</div>\n</div>'):format(NOTES_CLASS, notes) end local function split_args(args) local top_args, bottom_args = {}, {} for k, v in pairs(args or {}) do if bottom_params[k] then bottom_args[k] = v else top_args[k] = v end end return top_args, bottom_args end -- Builds a complete table around `contents`. function export.make_table(args, contents) local top_args, bottom_args = split_args(args) return export.make_top(top_args) .. contents .. export.make_bottom(bottom_args) end -- As export.make_table, for boxes. function export.make_box(args, contents) local top_args, bottom_args = split_args(args) return export.make_box_top(top_args) .. contents .. export.make_bottom(bottom_args) end -- Implementation of {{inflection-table-top}}. Other modules should call -- make_top directly instead. function export.top(frame) local args = M.parameters.process(frame:getParent().args, top_params, nil, "inflection-table", "top") return export.make_top(args) end -- Implementation of {{inflection-table-bottom}}. See the note on export.top. function export.bottom(frame) local args = M.parameters.process(frame:getParent().args, bottom_params, nil, "inflection-table", "bottom") return export.make_bottom(args) end -- Implementation of {{inflection-box-top}}. See the note on export.top. function export.box_top(frame) local args = M.parameters.process(frame:getParent().args, box_top_params, nil, "inflection-table", "box_top") return export.make_box_top(args) end -- Boxes close exactly like plain tables, so these are aliases rather than -- separate implementations. export.make_box_bottom = export.make_bottom export.box_bottom = export.bottom return export mas35tbdkeuyo7w6frl7ehgr6r1c24b Module:module loader 828 7966 54699 2026-09-25T16:55:47Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[==[ intro: This module lets a module declare all of its dependencies at the top, in one place, while still loading each of them only when the code reaches it. A page which never enters a given branch never pays for the modules that branch needs. Call `init` once and keep the table it returns: ``` local M = require("Module:module loader").init{ require = { utilities = "Module:utilities", languages = "Module:languages", }, loadData = { headword_data =...' 54699 Scribunto text/plain --[==[ intro: This module lets a module declare all of its dependencies at the top, in one place, while still loading each of them only when the code reaches it. A page which never enters a given branch never pays for the modules that branch needs. Call `init` once and keep the table it returns: ``` local M = require("Module:module loader").init{ require = { utilities = "Module:utilities", languages = "Module:languages", }, loadData = { headword_data = "Module:headword/data", }, lazy = { lang = function() return require("Module:languages").getByCode("de") end, }, } M.utilities.format_categories(...) -- requires Module:utilities M.headword_data.page -- mw.loadData on first access M.lang:makeEntryName(...) -- runs the getter on first access ``` The table `init` returns is a proxy. Reading a name for the first time does the work, and the result is {rawset} onto the proxy, so every later read is a plain table lookup and no metamethod runs. The same applies one level down: for a `require` entry, reading `M.<var>name</var>.<var>member</var>` caches that member on the sub-proxy. What a name reads as depends on the field it was declared in: * `require` and `requireOptional` give a sub-proxy, not the module table. Key lookups pass through, so the module's own {__index} still fires, but only keys already read exist on the proxy itself, so {pairs} and {#} see only those. Metamethods like {__call} apply to the module, not to the proxy that stands in for it. * `loadData` and `loadDataOptional` give the {mw.loadData} table itself, which behaves like any other table, so iteration works. * `lazy` gives whatever the getter returned. A name which was never declared reads as {nil}, as does an optional module which failed to load. The failure is cached, so a missing module is looked up once and not on every access. Guard with {if M.foo then} instead of indexing straight into it. A module declared under `require` which returns a function rather than a table is cached and returned as that function, with no proxy around it. The laziness is per-access, not per-call. The proxy defers until a name is read, not until the value that name holds is called, so binding a name to a local at the top of a module loads it there and then: ``` local shallowCopy = M.table.shallowCopy -- loads Module:table immediately ``` This applies to every field, `lazy` included, since {local x = M.x} is itself the access which runs the getter. Keep the access at the call site instead, where it costs two table lookups once the first access has cached both levels. Where a bare local is wanted, use the self-overwriting loader idiom of [[Module:table]] and [[Module:parameters]], which defers to the first call and costs nothing after it, because the local is replaced by the target function: ``` local function shallowCopy(...) shallowCopy = require("Module:table").shallowCopy return shallowCopy(...) end ``` ]==] local export = {} local NIL = {} -- sentinel for optional loadData/require that failed (so we don't retry) -- Validate the declaration: a short name may appear in only one of the five fields, and a -- module path may not appear twice across require and loadData. local function validate(modules, data_modules, optional_data_modules, optional_require_modules, lazy_modules) local seen_names = {} local seen_paths = {} -- Check require modules for name, path in pairs(modules) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "require" if seen_paths[path] then error("Module loader: module '" .. path .. "' loaded twice (as '" .. seen_paths[path] .. "' and '" .. name .. "')") end seen_paths[path] = name end -- Check loadData modules for name, path in pairs(data_modules) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "' (in both require and loadData)") end seen_names[name] = "loadData" if seen_paths[path] then error("Module loader: module '" .. path .. "' loaded twice (as '" .. seen_paths[path] .. "' and '" .. name .. "')") end seen_paths[path] = name end -- Check loadDataOptional (names only; its paths are not tracked) for name, path in pairs(optional_data_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "loadDataOptional" end -- Check requireOptional (names only) for name, path in pairs(optional_require_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "requireOptional" end -- Check lazy (names only) for name in pairs(lazy_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "lazy" end end -- Build a proxy for a lazy object: table of name → function () → value. Each key is evaluated once and cached. local function make_lazy_object_proxy(getters) return setmetatable({}, { __index = function(proxy, key) local getter = getters[key] if type(getter) ~= "function" then return nil end local value = getter() rawset(proxy, key, value) return value end }) end --[==[ Given a table of dependency declarations ``opts``, returns the lazy proxy described above. Call it once, at the top of a module. ``opts`` may contain any of the following fields, all optional: * `require`: maps a short name to a module path, which is loaded with {require} on first access. * `loadData`: maps a short name to a data module path, which is loaded with {mw.loadData} on first access. The table is returned as-is. * `requireOptional`: as `require`, but the load runs under {pcall}, and a module which is missing or throws an error reads as {nil}. Such a module must return a table; one returning a function counts as missing. * `loadDataOptional`: as `loadData`, but the load runs under {pcall}, and a module which is missing or throws an error reads as {nil}. * `lazy`: maps a short name to a getter for a value which is not a whole module. Where `require` and `loadData` defer loading a module, `lazy` defers computing a value, such as an object built from a module, or something borrowed from elsewhere which you would rather not touch until it is needed. Each entry is either a function returning the value, or a table of names to such functions, giving a nested object whose keys are each evaluated on their own first access. For instance: ** {lang = function() return require("Module:languages").getByCode("de") end}, so that [[Module:languages]] is not loaded on pages which never reach the relevant branch. ** {ustring = function() return mw.ustring end}, a sub-namespace of {mw}. ** {concat = function() return table.concat end}, a plain alias. ** {mw = {getCurrentTitle = ..., getCurrentFrame = ..., getContentLanguage = ...}}, the nested form, with one deferred key per expensive call. Throws an error if a short name is declared in more than one field, or if the same module path is declared twice across `require` and `loadData`. Paths in the optional fields are not tracked, so a module may be declared both as required and as optional.]==] function export.init(opts) local modules = opts.require or {} local data_modules = opts.loadData or {} local optional_data_modules = opts.loadDataOptional or {} local optional_require_modules = opts.requireOptional or {} local lazy_modules = opts.lazy or {} validate(modules, data_modules, optional_data_modules, optional_require_modules, lazy_modules) local loaded_modules = {} local function get_module(module_name) local cached = loaded_modules[module_name] if cached ~= nil then if cached == NIL then return nil end return cached end if data_modules[module_name] then loaded_modules[module_name] = mw.loadData(data_modules[module_name]) elseif optional_data_modules[module_name] then local ok, data = pcall(mw.loadData, optional_data_modules[module_name]) loaded_modules[module_name] = (ok and data) and data or NIL elseif optional_require_modules[module_name] then local ok, mod = pcall(require, optional_require_modules[module_name]) loaded_modules[module_name] = (ok and mod and type(mod) == "table") and mod or NIL elseif modules[module_name] then loaded_modules[module_name] = require(modules[module_name]) end local m = loaded_modules[module_name] if m == NIL then return nil end return m end local proxy_metatable = { __index = function(proxy, module_name) -- Lazy: single getter function or table of getters (nested lazy object) if lazy_modules[module_name] then local lazy_def = lazy_modules[module_name] if type(lazy_def) == "function" then local value = lazy_def() rawset(proxy, module_name, value) return value end if type(lazy_def) == "table" then local sub = make_lazy_object_proxy(lazy_def) rawset(proxy, module_name, sub) return sub end end local module = get_module(module_name) if not module then return nil end -- If the module itself is a function, cache and return it directly if type(module) == "function" then rawset(proxy, module_name, module) return module end -- For loadData / loadDataOptional modules, return the table directly (no function proxying) if data_modules[module_name] or optional_data_modules[module_name] then rawset(proxy, module_name, module) return module end -- Everything else (require, and requireOptional that loaded) gets a proxy that -- caches individual members as they are read. local function_proxy = setmetatable({}, { __index = function(function_cache, function_name) local cached_function = module[function_name] rawset(function_cache, function_name, cached_function) return cached_function end }) rawset(proxy, module_name, function_proxy) return function_proxy end } return setmetatable({}, proxy_metatable) end return export 4yt8l6eou5zun5d9uyqgkh1c1u1w3d4 Module:utilities/templates 828 7967 54700 2026-09-25T17:33:46Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local debug_track_module = "Module:debug/track" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local utilities_format_categories_with_sort_keys_module = "Module:utilities/format_categories_with_sort_keys" local concat = table.concat local insert = table.insert local require = require local function format_categories(...) format_categories = require(utilities_module).format_categories return format_...' 54700 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local utilities_format_categories_with_sort_keys_module = "Module:utilities/format_categories_with_sort_keys" local concat = table.concat local insert = table.insert local require = require local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_categories_with_sort_keys(...) format_categories_with_sort_keys = require(utilities_format_categories_with_sort_keys_module) return format_categories_with_sort_keys(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function track(...) track = require(debug_track_module) return track(...) end -- Used by {{catfix}}. function export.catfix(frame) local args = process_params(frame:getParent().args, { [1] = {type = "language", required = true}, [2] = {alias_of = "sc"}, ["sc"] = {type = "script"}, }) return require("Module:utilities").catfix(args[1], args.sc) end -- Used by {{categorize}}, {{catlangname}} and {{topics}}. function export.categorize(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und", sublist = true}, [2] = {required = true, list = true, allow_holes = true}, sort = {list = true, separate_no_index = true, allow_holes = true}, force = {type = "boolean"}, }) local langs = args[1] if not langs[1] then return "" end local parts = {} for _, lang in ipairs(langs) do local full_langcode = lang:getFullCode() if lang:getCode() ~= full_langcode then track("Module:utilities/templates/categorize called with variant langcode") end local raw_cats, sort_keys, format = args[2], args.sort, frame.args["format"] local default_sort = sort_keys.default local prefix = format == "pos" and lang:getFullName() .. " " or format == "topic" and full_langcode .. ":" or "" -- Put the categories in an array. If any have an individual sortkey, they -- will need to be tables with the category and sort key for -- [[Module:utilities/format_categories_with_sort_keys]]; otherwise, add -- them as strings. local cats, n, with_sort_keys = {}, 0, false for i = 1, raw_cats.maxindex do local cat = raw_cats[i] if cat ~= nil then cat = prefix .. cat local sort_key = sort_keys[i] if with_sort_keys then cat = {category = cat, sort_key = sort_key} -- If a sort key exists, reformat all previously-processed -- categories into the table format. elseif sort_key ~= nil then with_sort_keys = true for j = 1, n do cats[j] = {category = cats[j]} end cat = {category = cat, sort_key = sort_key} end n = n + 1 cats[n] = cat end end if with_sort_keys then insert(parts, format_categories_with_sort_keys(cats, lang, default_sort, nil, args.force)) else insert(parts, format_categories(cats, lang, default_sort, nil, args.force)) end end return concat(parts) end return export s31ss6vfmg5spqyg1qbe9bav60gd1kv Bysen:catlangname 10 7968 54701 2026-09-25T17:34:39Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:utilities/templates|categorize|format=pos}}<!--' 54701 wikitext text/x-wiki {{#invoke:utilities/templates|categorize|format=pos}}<!-- kntw5xt8bsnyfvjk0hbms2jftyn9iff Module:languages/data/3/u 828 7969 54702 2026-09-25T17:35:46Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["uam"] = { "Uamué", 3441418, } m["uan"] = { "Kuan", 6441085, } m["uar"] = { "Tairuma", 7676386, "paa-eel", "Latn", } m["uba"] = { "Ubang", 39...' 54702 Scribunto text/plain local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["uam"] = { "Uamué", 3441418, } m["uan"] = { "Kuan", 6441085, } m["uar"] = { "Tairuma", 7676386, "paa-eel", "Latn", } m["uba"] = { "Ubang", 3914467, "nic-ben", "Latn", } m["ubi"] = { "Ubi", 56264, "cdc-est", "Latn", } m["ubl"] = { "Buhi'non Bikol", 18664494, "phi", "Latn", } m["ubr"] = { "Ubir", 3547642, "poz-ocw", "Latn", } m["ubu"] = { "Umbu-Ungu", 12953245, "ngf-hag", "Latn", } m["uby"] = { "Ubykh", 36931, "cau-nwc", "Cyrl, Latn", translit = "uby-translit", override_translit = true, display_text = {Cyrl = s["cau-Cyrl-displaytext"]}, strip_diacritics = { Cyrl = s["cau-Cyrl-stripdiacritics"], Latn = s["cau-Latn-stripdiacritics"], }, sort_key = "uby-sortkey", } m["uda"] = { "Uda", 11011951, "nic-lcr", } m["ude"] = { "Udihe", 13235, "tuw-udg", "Cyrl", } m["udg"] = { "Muduga", 16886762, "dra-imd", "Mlym", -- Mlym translit in [[Module:scripts/data]] } m["udi"] = { "Udi", 36867, "cau-esm", "Cyrl, Latn, Armn, Geor", ancestors = "xag", translit = { Cyrl = "udi-translit", -- Geor, Armn translit in [[Module:scripts/data]] }, override_translit = true, display_text = {Cyrl = s["cau-Cyrl-displaytext"]}, strip_diacritics = { Cyrl = s["cau-Cyrl-stripdiacritics"], Latn = s["cau-Latn-stripdiacritics"], }, } m["udj"] = { "Ujir", 14916906, "poz-cet", "Latn", } m["udl"] = { "Uldeme", 3515078, "cdc-cbm", "Latn", } m["udm"] = { "Udmurt", 13238, "urj-prm", "Cyrl", translit = "udm-translit", override_translit = true, sort_key = "udm-sortkey", } m["udu"] = { "Uduk", 3182573, "ssa-kom", } m["ues"] = { "Kioko", 18343036, } m["ufi"] = { "Ufim", 7877531, "ngf-gmo", "Latn", } m["uga"] = { "Ugaritic", 36928, "sem-nwe", "Ugar", translit = { Ugar = "uga-translit", } } m["ugb"] = { "Kuku-Ugbanh", 10549854, } m["uge"] = { "Ughele", 966303, "poz-ocw", } m["ugn"] = { "Ugandan Sign Language", 7877677, "sgn-asl", } m["ugo"] = { "Gong", 3448919, "tbq-lob", "Thai", sort_key = "Thai-sortkey", } m["ugy"] = { "Uruguayan Sign Language", 7901470, "sgn", } m["uha"] = { "Uhami", 3913328, "alv-nwd", "Latn", } m["uhn"] = { "Damal", 4748974, "qfa-dis", -- ngf? or uncertain "Latn", } m["uis"] = { "Uisai", 7878123, "paa-sbo", "Latn", } m["uiv"] = { "Iyive", 11128658, "nic-tvc", "Latn", } m["uji"] = { "Tanjijili", 3914939, "nic-pls", "Latn", } m["uka"] = { "Kaburi", 6344482, "ngf-sbh", "Latn", } m["ukg"] = { "Ukuriguma", 7878623, "ngf-num", "Latn", } m["ukh"] = { "Ukhwejo", 36623, "bnt-bek", "Latn", } m["ukk"] = { "Muak Sa-aak", 23807993, "mkh-pal", "Latn", } m["ukl"] = { "Ukrainian Sign Language", 10322106, "sgn", } m["ukp"] = { "Ukpe-Bayobiri", 3914470, "nic-ben", "Latn", } m["ukq"] = { "Ukwa", 7878635, "nic-ief", "Latn", } m["uks"] = { "Kaapor Sign Language", 3322101, "sgn", } m["uku"] = { "Ukue", 3913387, "alv-nwd", "Latn", } m["ukw"] = { "Ukwuani-Aboh-Ndoni", 36636, "alv", "Latn", } m["uky"] = { "Kuuk Yak", 6448719, "aus-psw", "Latn", } m["ula"] = { "Fungwa", 5509187, "nic-shi", "Latn", } m["ulb"] = { "Olukumi", 36722, "alv-yor", "Latn", strip_diacritics = {Latn = {remove_diacritics = c.grave .. c.acute .. c.macron}}, sort_key = { from = {"ch", "ẹ", "gb", "gh", "gw", "kp", "kw", "ọ", "ṣ"}, to = {"c" .. p[1], "e" .. p[1], "g" .. p[1], "g" .. p[2], "g" .. p[3], "k" .. p[1], "k" .. p[2], "o" .. p[1], "s" .. p[1]} }, } m["ulc"] = { "Ulch", 13239, "tuw-nan", "Cyrl, Latn", strip_diacritics = { from = {"['’]"}, to = {"ʼ"} }, sort_key = "ulc-sortkey", } m["ule"] = { "Lule", 12635889, nil, "Latn", } m["ulf"] = { "Afra", 4477735, "qfa-dis", -- Papuan; extinct and poorly documented; per Wurm (1975), an independent branch of TNG; per Ross (2005), -- unclassifiable; per Usher (2020), West Pauwasi, though divergent; per Foley (2018), isolate. "Latn", } m["uli"] = { "Ulithian", 36842, "poz-mic", "Latn", } m["ulk"] = { "Meriam", 788174, "paa-etf", "Latn", } m["ull"] = { "Ullatan", 8761579, "dra-mal", } m["ulm"] = { "Ulumanda'", 3501892, "poz-ssw", "Latn", } m["uln"] = { "Unserdeutsch", 13244, "crp", "Latn", ancestors = "de", } m["ulu"] = { "Uma' Lung", 3548186, "poz-swa", } m["ulw"] = { "Ulwa (Nicaragua)", 2405552, "nai-min", "Latn", } m["uma"] = { "Umatilla", 12953952, "nai-shp", "Latn", ancestors = "nai-spt", } m["umb"] = { "Umbundu", 36983, "bnt", "Latn", } m["umc"] = { "Marrucinian", 36110, "itc-sbl", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["umd"] = { "Umbindhamu", 7881346, "aus-pmn", "Latn", } m["umg"] = { "Umbuygamu", 3915677, "aus-pmn", "Latn", } m["umi"] = { "Ukit", 7878321, nil, "Latn", } m["umm"] = { "Umon", 3915448, "nic-ucn", "Latn", } m["umn"] = { "Makyan Naga", 6740516, "sit-kch", } m["umo"] = { "Umotína", 7881740, "sai-mje", } m["ump"] = { "Umpila", 12953954, "aus-pmn", "Latn", } m["umr"] = { "Umbugarla", 2980392, } m["ums"] = { "Pendau", 7162371, "poz-tot", "Latn", } m["umu"] = { "Munsee", 56547, "del", "Latn", strip_diacritics = {remove_diacritics = c.acute .. c.breve}, } m["una"] = { "North Watut", 15887898, "poz-ocw", "Latn", } m["und"] = { "Undetermined", 22282914, "qfa-not", "All", } m["une"] = { "Uneme", 3913357, "alv-yek", "Latn", } m["ung"] = { "Ngarinyin", 1284885, "aus-wor", "Latn", } m["uni"] = { "Uni", 65043886, "paa-pio", "Latn", } m["unk"] = { "Enawené-Nawé", 3307184, "awd", "Latn", } m["unm"] = { "Unami", 3549180, "del", "Latn", --[===[Don't strip diacritics from display text, per [[WT:Grease pit/2020/May]]. strip_diacritics = {remove_diacritics = c.grave .. c.diaer},]===] } m["unn"] = { "Kurnai", 61676882, "aus-pam", "Latn", } m["unr"] = { "Mundari", 3327828, "mun", "Nagm, Deva, Onao", -- Onao is used by Bhumij, which may be a separate language; remove if it gets split out translit = "hi-translit", -- for now } m["unu"] = { "Unubahe", 7897776, } m["unx"] = { "Munda", 36264959, "mun", "Latn", } m["unz"] = { "Unde Kaili", 12953596, "poz-kal", "Latn", } m["uok"] = { "Uokha", 3441216, "alv-edo", "Latn", } m["uon"] = { "Kulon", 11182000, "map", "Latn", } m["upi"] = { "Umeda", 7881465, "paa-war", "Latn", } m["upv"] = { "Northeast Malakula", 13249, "poz-vnc", "Latn", } m["ura"] = { "Urarina", 1579560, } m["urb"] = { "Urubú-Kaapor", 13893353, "tup-gua", "Latn", } m["urc"] = { "Urningangg", 10710522, } m["ure"] = { "Uru", 2992892, } m["urf"] = { "Uradhi", 3915680, "aus-pam", "Latn", } m["urg"] = { "Urigina", 7900603, "ngf-pek", "Latn", } m["urh"] = { "Urhobo", 36663, "alv-swd", "Latn", } m["uri"] = { "Urim", 7900609, "paa-trr", "Latn", } m["urk"] = { "Urak Lawoi'", 7899573, "poz-mly", "Thai", sort_key = "Thai-sortkey", } m["url"] = { "Urali", 7899602, "dra-kod", "Knda", -- Knda translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } m["urm"] = { "Urapmin", 7899769, "ngf-mok", "Latn", } m["urn"] = { "Uruangnirin", 7901389, "poz-cet", "Latn", } m["uro"] = { "Ura (New Guinea)", 3121049, "paa-bai", "Latn", } m["urp"] = { "Uru-Pa-In", 7901376, "tup-gua", "Latn", } m["urr"] = { "Löyöp", 3272124, "poz-vnn", "Latn", } m["urt"] = { "Urat", 3502084, "paa-trr", "Latn", } m["uru"] = { "Urumi", 7901530, "tup", "Latn", } m["urv"] = { "Uruava", 36875, "poz-ocw", "Latn", } m["urw"] = { "Sop", 7562808, "ngf-pek", "Latn", } m["urx"] = { "Urimo", 7900611, "paa-mar", "Latn", } m["ury"] = { "Orya", 7105295, "paa-too", "Latn", } m["urz"] = { "Uru-Eu-Wau-Wau", 10266012, "tup-gua", "Latn", } m["usa"] = { "Usarufa", 7901714, "ngf-gau", "Latn", } m["ush"] = { "Ushojo", 3540446, "inc-shn", "Aran", } m["usi"] = { "Usui", 12644231, } m["usk"] = { "Usaghade", 3914048, "nic-lcr", "Latn", } m["usp"] = { "Uspanteco", 36728, "myn", "Latn", } m["uss"] = { "Saare", 63313662, "nic-knn", "Latn", } m["usu"] = { "Uya", 7904082, "ngf-nur", "Latn", } m["uta"] = { "Otank", 3913990, "nic-tvc", "Latn", } m["ute"] = { "Ute", 13260, "azc-num", "Latn", } m["uth"] = { "Hun", 63313668, "nic-knn", "Latn", } m["utp"] = { "Aba", 2841465, "poz-tem", "Latn", } m["utr"] = { "Etulo", 35262, "alv-ido", "Latn", } m["utu"] = { "Utu", 7903469, "ngf-usi", "Latn", } m["uum"] = { "Urum", 13257, "trk-kcu", "Cyrl", translit = "uum-translit", } m["uur"] = { "Ura (Vanuatu)", 7899531, "poz-vns", "Latn", } m["uuu"] = { "U", 953082, "mkh-pal", } m["uve"] = { "West Uvean", 36837, "poz-pnp", "Latn", } m["uvh"] = { "Uri", 7900540, "ngf-era", "Latn", } m["uvl"] = { "Lote", 3259972, "poz-ocw", "Latn", } m["uwa"] = { "Kuku-Uwanh", 3915687, "aus-pmn", "Latn", } m["uya"] = { "Doko-Uyanga", 7904095, "nic-ucr", "Latn", } return require("Module:languages").finalizeData(m, "language") 8xj4ngias73rovpdc4g8tqswp67hy6j Module:languages/chars 828 7970 54704 2026-09-25T17:39:37Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local table = table local insert = table.insert local u = require("Module:string/char") -- UTF-8 encoded strings for some commonly-used diacritics. local c = { prime = u(0x02B9), grave = u(0x0300), acute = u(0x0301), circ = u(0x0302), -- circumflex tilde = u(0x0303), macron = u(0x0304), overline = u(0x0305), breve = u(0x0306), dotabove = u(0x0307), diaer = u(0x0308), -- diaeresis ringabove = u(0x030A),...' 54704 Scribunto text/plain local export = {} local table = table local insert = table.insert local u = require("Module:string/char") -- UTF-8 encoded strings for some commonly-used diacritics. local c = { prime = u(0x02B9), grave = u(0x0300), acute = u(0x0301), circ = u(0x0302), -- circumflex tilde = u(0x0303), macron = u(0x0304), overline = u(0x0305), breve = u(0x0306), dotabove = u(0x0307), diaer = u(0x0308), -- diaeresis ringabove = u(0x030A), dacute = u(0x030B), -- double acute caron = u(0x030C), lineabove = u(0x030D), dgrave = u(0x030F), -- double grave invbreve = u(0x0311), -- inverted breve turnedcommaabove = u(0x0312), commaabove = u(0x0313), revcommaabove = u(0x0314), -- reversed comma above dotbelow = u(0x0323), diaerbelow = u(0x0324), -- diaeresis below ringbelow = u(0x0325), commabelow = u(0x0326), cedilla = u(0x0327), ogonek = u(0x0328), caronbelow = u(0x032C), brevebelow = u(0x032E), macronbelow = u(0x0331), perispomeni = u(0x0342), ypogegrammeni = u(0x0345), CGJ = u(0x034F), -- combining grapheme joiner zigzag = u(0x035B), dbrevebelow = u(0x035C), -- double breve below dmacron = u(0x035E), -- double macron dtilde = u(0x0360), -- double tilde dinvbreve = u(0x0361), -- double inverted breve small_a = u(0x0363), small_e = u(0x0364), small_i = u(0x0365), small_o = u(0x0366), small_u = u(0x0367), keraia = u(0x0374), lowerkeraia = u(0x0375), tonos = u(0x0384), palatalization = u(0x0484), dasiapneumata = u(0x0485), psilipneumata = u(0x0486), kashida = u(0x0640), fathatan = u(0x064B), dammatan = u(0x064C), kasratan = u(0x064D), fatha = u(0x064E), damma = u(0x064F), kasra = u(0x0650), shadda = u(0x0651), sukun = u(0x0652), hamzaabove = u(0x0654), nunghunna = u(0x0658), zwarakay = u(0x0659), smallv = u(0x065A), superalef = u(0x0670), udatta = u(0x0951), anudatta = u(0x0952), tacute = u(0x1ACB), -- triple acute dsvarita = u(0x1CDA), -- double svarita tsvarita = u(0x1CDB), -- triple svarita dottedgrave = u(0x1DC0), dottedacute = u(0x1DC1), coronis = u(0x1FBD), psili = u(0x1FBF), dasia = u(0x1FEF), ZWNJ = u(0x200C), -- zero width non-joiner ZWJ = u(0x200D), -- zero width joiner RSQuo = u(0x2019), -- right single quote kavyka = u(0xA67C), VS01 = u(0xFE00), -- variation selector 1 -- Punctuation for the standard_chars field. -- Note: characters are literal (i.e. no magic characters). punc = " ',-​‌‍‐‑‒–—…∅◌", -- Range covering all diacritics. diacritics = u(0x300) .. "-" .. u(0x34E) .. u(0x350) .. "-" .. u(0x36F) .. u(0x1AB0) .. "-" .. u(0x1ACE) .. u(0x1DC0) .. "-" .. u(0x1DFF) .. u(0x20D0) .. "-" .. u(0x20F0) .. u(0xFE20) .. "-" .. u(0xFE2F), } -- Braille characters for the standard_chars field. local braille = {} for i = 0x2800, 0x28FF do insert(braille, u(i)) end c.braille = table.concat(braille) export.chars = c -- Pre-generate PUA characters, generally used in sortkeys. -- Note: if changing the amount, use powers of 2 to maximise memory efficiency (due to the way memory is allocated for tables). local p = {} for i = 1, 32 do p[i] = u(0xF000+i-1) end export.puaChars = p local cs = {} -- Used in the default strip_diacritics and sort_key for Cyrs, but also used directly by Old Ruthenian (zle-ort). cs["Cyrs_remove_diacritics"] = c.grave .. c.acute .. c.dotabove .. c.diaer .. c.invbreve .. c.palatalization .. c.dasiapneumata .. c.psilipneumata .. c.dottedgrave .. c.dottedacute .. c.kavyka export.chars_substitutions = cs return export 4jo6gnddmlyjm1f3me7obw2m2tjok4g gedreóh 0 7971 54705 2026-09-25T17:45:49Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{sprǣc|ang}} ===Rihstefn=== * IPA: /jeˈdre͜oːx/ ===Tōgeīecendlice=== '''gedreóh''' # nāht druncen ===Declīnung=== {{ang-adecl|gedreóh}} ===Wendunga=== * Nīwenglisc: [[sober]]' 54705 wikitext text/x-wiki {{sprǣc|ang}} ===Rihstefn=== * IPA: /jeˈdre͜oːx/ ===Tōgeīecendlice=== '''gedreóh''' # nāht druncen ===Declīnung=== {{ang-adecl|gedreóh}} ===Wendunga=== * Nīwenglisc: [[sober]] qclzcpekewswcs6d4zaxt5a9furdbun 54706 54705 2026-09-25T17:47:12Z Deadend0914 7211 54706 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihstefn=== * IPA: /jeˈdre͜oːx/ ===Tōgeīecendlice=== '''gedreóh''' # nāht druncen ===Declīnung=== {{ang-adecl|gedreóh}} ===Wendunga=== * Nīwenglisc: [[sober]] [[Flocc: Englisc tōgeīecendlic]] kw4outog3gsn48jnogutk4e70s7si04 54712 54706 2026-09-25T17:53:52Z Deadend0914 7211 /* Declīnung */ 54712 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihstefn=== * IPA: /jeˈdre͜oːx/ ===Tōgeīecendlice=== '''gedreóh''' # nāht druncen ===Declīnung=== {{ang-adecl|gedreóh}} ===Wendunga=== * Nīwenglisc: [[sober]] [[Flocc: Englisc tōgeīecendlic]] 4e86beazh6xoxx2jfe7hmt9d3ymv3ak Module:debug 828 7972 54707 2026-09-25T17:48:11Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local byte = string.byte local concat = table.concat local escape -- defined below local format = string.format local gsub = string.gsub local insert = table.insert local match = string.match local sub = string.sub local toNFC = mw.ustring.toNFC local function is_array(...) is_array = require(table_module).isArray return is_array(...) end local fu...' 54707 Scribunto text/plain local export = {} local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local byte = string.byte local concat = table.concat local escape -- defined below local format = string.format local gsub = string.gsub local insert = table.insert local match = string.match local sub = string.sub local toNFC = mw.ustring.toNFC local function is_array(...) is_array = require(table_module).isArray return is_array(...) end local function isutf8(...) isutf8 = require(string_utilities_module).isutf8 return isutf8(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function table_size(...) table_size = require(table_module).size return table_size(...) end do local escapes local function get_escapes() escapes, get_escapes = { ["\a"] = [[\a]], ["\b"] = [[\b]], ["\t"] = [[\t]], ["\n"] = [[\n]], ["\v"] = [[\v]], ["\f"] = [[\f]], ["\r"] = [[\r]], ["\""] = [[\"]], ["'"] = [[\']], ["\\"] = [[\\]], }, nil return escapes end local function escape_byte(ch) return (escapes or get_escapes())[ch] or format("\\%03d", byte(ch)) end local function escape_bytes(ch) return (gsub(ch, ".", escape_byte)) end local function escape_char(ch) local ch_len = #ch if ch_len == 1 then return escape_byte(ch) end local b = byte(ch) -- Matching bytes below \128 are all to be escaped, \128 to \191 can't -- be leading bytes in UTF-8, \192 and \193 could only occur in overlong -- encodings, so can't occur in UTF-8, U+0080 (\194\128) to U+009F -- (\194\159) are control characters, U+00A0 (\194\160) is the no-break -- space, and \245 to \255 could only occur in encodings for codepoints -- above U+10FFFF, so can't occur in UTF-8. if b < 194 or b > 244 or (b == 194 and byte(ch, 2) < 161) then return escape_bytes(ch) -- 2-byte encodings starting \194 to \223 are all valid, so no need to -- check them with isutf8(). If there are additional trailing -- bytes, escape them. elseif b < 224 then return ch_len == 2 and ch or (sub(ch, 1, 2) .. escape_bytes(sub(ch, 3))) end -- Check 3- and 4-byte encodings with isutf8(), as they might be -- invalid due to overlong encodings or being above U+10FFFF. As above, -- escape any additional trailing bytes. local n = b < 240 and 3 or 4 if ch_len == n then return isutf8(ch) and ch or escape_bytes(ch) elseif ch_len > n then local init_ch = sub(ch, 1, n) if isutf8(init_ch) then return init_ch .. escape_bytes(sub(ch, n + 1)) end end return escape_bytes(ch) end local function escape_non_NFC(str) local normalized = toNFC(str) if normalized == str then return str end local str_len, i, start, offset, output = #str, 1, 1, 0 while i <= str_len do local b = byte(str, i) if b == byte(normalized, i + offset) then i = i + 1 else if output == nil then output = {} end -- Backtrack to the start of the character. while b >= 128 and b < 192 do i = i - 1 b = byte(str, i) end -- Insert any intermediate characters up to this point. if start ~= i then insert(output, sub(str, start, i - 1)) end -- Get the first character, then find the sequence of characters -- which differs from the normalized string. local seq = match(str, "^.[\128-\191]*", i) -- Find the raw sequence and the normalized sequence by adding -- a character at a time to the raw sequence, and checking if -- it matches the current point in the normalized string. -- This is necessary to ensure that the offset between the two -- strings is correct, when comparing equivalent sections. local seq_len, poss_seq, norm_seq = #seq, seq while true do if not norm_seq then norm_seq = match(normalized, "^" .. toNFC(poss_seq), i + offset) -- Once a matching sequence has been found, check if it's -- still possible to match the same normalized sequence with -- a longer raw sequence, as form NFC will have taken the -- longest sequence when normalizing the input. elseif toNFC(poss_seq) ~= norm_seq then break end seq, seq_len = poss_seq, #poss_seq local nxt_ch = match(str, "^.[\128-\191]*", i + seq_len) if nxt_ch == nil then break end poss_seq = poss_seq .. nxt_ch end -- Modify the offset to account for the difference in length -- between the two sequences. Usually, the NFC form will be -- shorter, but in rare cases it is longer (e.g. U+0F73 -- normalizes to U+0F71 + U+0F72). offset = offset + #norm_seq - seq_len i = i + seq_len start = i -- Escape the non-ASCII portion of the sequence. This ensures -- that escapes added by escape_char don't end up double-escaped -- if they would otherwise be modified by form NFC; e.g. "\n" + -- U+0303 ("\ñ") needs to avoid escaping the "n". if seq ~= "" then insert(output, (gsub(seq, "[\128-\255]", escape_byte))) end end end if output == nil then return str end insert(output, sub(str, start)) return concat(output) end -- Escapes control characters, backslash, double quote, the no-break space, -- bytes that aren't used in UTF-8, invalid UTF-8 character sequences, and -- any bytes necessary to ensure that the output is Unicode form NFC, -- because MediaWiki automatically converts page content to form NFC; e.g. -- "e" + U+0301 ("é") results in "e\204\129", because otherwise the sequence -- would be converted to "é" (U+00E9)); this ensures that results can be -- relied upon to be stable if saved as part of page content. function export.escape(str) return escape_non_NFC(gsub(str, "[%c\"'\\\128-\255][\128-\191]*", escape_char)) end escape = export.escape end -- Convert a value to a string function export.dump(value, prefix, tsort) local t = type(value) prefix = prefix or "" if t == "string" then return '"' .. escape(value) .. '"' elseif t == "table" then local str_table = {} insert(str_table, " {") for key, val in sorted_pairs(value, tsort) do insert(str_table, " " .. prefix .. "\t[" .. export.dump(key, prefix .. "\t") .. "] = " .. gsub(export.dump(val, prefix .. "\t"), "^ ", "") .. ",") end insert(str_table, " " .. prefix .. "}") return concat(str_table, "\n") else return tostring(value) end end function export.highlight_dump(value, prefix, tsort, options) options = options or {} local func = options.modified and "modified_dump" or "dump" local dump = export[func](value, prefix, tsort) -- Remove spaces at beginnings of lines (which are simply to force a <pre></pre> tag). dump = gsub(dump, "%f[^%z\n] ", "") return export.highlight(dump) end -- Returns true if table contains a table as one of its values local function containsTable(t) for _, value in pairs(t) do if type(value) == "table" then return true end end return false end local function containsTablesWithSize(t, size) for _, value in pairs(t) do if type(value) == "table" and table_size(value) ~= size then return false end end return true end --[=[ Convert a value to a string. Like dump below, but if a table has consecutive numbered keys and does not have a table as one of its values, it will be placed on a single line. Used by [[Module:User:Erutuon/script recognition]]. ]=] function export.modified_dump(value, prefix, tsort) local t = type(value) prefix = prefix or "" if t == "string" then return '"' .. value .. '"' elseif t == "table" then local str_table = {} local containsTable = containsTable(value) local consecutive = is_array(value) if consecutive and not containsTable or containsTable and containsTablesWithSize(value, 3) then insert(str_table, "{") for key, val in sorted_pairs(value, tsort) do if containsTable then insert(str_table, "\n\t" .. prefix) else insert(str_table, " ") end if type(key) == "string" then insert(str_table, "[" .. export.modified_dump(key) .. "] = ") end insert(str_table, type(key) == "number" and type(val) == "number" and format("0x%05X", val) or export.modified_dump(val)) if not (consecutive and #value == 3) or type(key) == "number" and value[key + 1] then insert(str_table, ",") end end if containsTable then insert(str_table, "\n" .. prefix) else insert(str_table, " ") end insert(str_table, "}") return concat(str_table) end insert(str_table, " {") for key, val in sorted_pairs(value, tsort) do insert(str_table, " " .. prefix .. "\t[" .. export.modified_dump(key, prefix .. "\t") .. "] = " .. gsub(export.modified_dump(val, prefix .. "\t"), "^ ", "") .. ",") end insert(str_table, " " .. prefix .. "}") return concat(str_table, "\n") elseif t == "number" and value > 46 then return format("0x%05X", value) else return tostring(value) end end export.track = require("Module:debug/track") -- Trigger a script error from a template function export.error(frame) error(frame.args[1] or "(no message specified)") end --[[ Convenience function for generating syntaxhighlight tags. Display defaults to block. Options is a table. To display inline text with HTML highlighting: { inline = true, lang = "html" } ]] function export.highlight(content, options) if type(content) == "table" then options = content options = { lang = options.lang or "lua", inline = options.inline and true } return function(content) return mw.getCurrentFrame():extensionTag("syntaxhighlight", content, options) end else return mw.getCurrentFrame():extensionTag("syntaxhighlight", content, { lang = options and options.lang or "lua", inline = options and options.inline and true or nil }) end end function export.track_unrecognized_args(args, template_name) local function track(code) export.track(template_name .. "/" .. code) end track("unrecognized arg") local arg_list = {} for arg, value in pairs(args) do track("unrecognized arg/" .. arg) insert(arg_list, format("|%s=%s", arg, value)) end mw.log(format("Unrecognized parameter%s in {{%s}}: %s.", arg_list[2] and "s" or "", template_name, concat(arg_list, ", ") )) end do local placeholder = "_message_" function export._placeholder_error(frame) -- A dummy function that throws an error with a placeholder message. error(placeholder, (frame.args.level or 1) + 6) end -- Throw an error via callParserFunction, which generates a real error with traceback, automatic categorization in [[CAT:E]] etc., but the error message is returned as a string. Then, replace the placeholder error message with `message`, which is preprocessed. This is necessary when preprocessing needs to be applied (e.g. when using <pre> tags), since otherwise strip markers and other half-processed text gets displayed instead. function export.formatted_error(message, level) local frame = mw.getCurrentFrame() return (frame:callParserFunction("#invoke", {"debug", "_placeholder_error", level = level}) :gsub(placeholder, frame:preprocess(message))) end end return export flmvh7jakbzodr3hhecvopt1m6pdzph Module:debug/track 828 7973 54708 2026-09-25T17:48:41Z Deadend0914 7211 Gesceop tramet þe hafaþ '-- TODO 1: normalize keys with leading spaces, which don't get removed when -- "Tracking/" is prefixed. -- TODO 2: avoid weird inputs like "", which don't register as invalid since -- "Tracking/" is a valid page title. -- TODO 3: use varargs instead of a table input, then recurse with track(). local title_make_title_module = "Module:title/makeTitle" -- Transclusion-based tracking as subpages of [[Wiktionary:Tracking]]. -- Tracked pages can be found at [[Speci...' 54708 Scribunto text/plain -- TODO 1: normalize keys with leading spaces, which don't get removed when -- "Tracking/" is prefixed. -- TODO 2: avoid weird inputs like "", which don't register as invalid since -- "Tracking/" is a valid page title. -- TODO 3: use varargs instead of a table input, then recurse with track(). local title_make_title_module = "Module:title/makeTitle" -- Transclusion-based tracking as subpages of [[Wiktionary:Tracking]]. -- Tracked pages can be found at [[Special:WhatLinksHere/Wiktionary:Tracking/KEY]]. local error = error local find = string.find local log = mw.log local sub = string.sub local type = type local invalid_tracking_key_key = "debug/track/invalid key" local function make_title(...) make_title = require(title_make_title_module) return make_title(...) end local memo local function track(key) if not memo then memo = {} -- Return if memoized. elseif memo[key] then return end -- Throw an error if `key` isn't a string. local key_type = type(key) if key_type ~= "string" then error("Tracking keys supplied to [[Module:debug/track]] must be strings; received " .. key_type .. ".", 3) end -- make_title returns nil for invalid titles, but "#" is treated as a -- fragment separator (e.g. "foo#bar" generates the title "foo"), so it -- needs to be manually excluded. local title = not find(key, "#", nil, true) and make_title(4, "Tracking/" .. key) if not title then -- Track uses of invalid keys. Instead of recursing, simply memoize the -- invalid key and replace it with the 'invalid tracking key' key. -- [[Special:WhatLinksHere/Wiktionary:Tracking/debug/track/invalid key]] log("Invalid tracking key: " .. key) memo[key] = true key = invalid_tracking_key_key if memo[key] then return end title = make_title(4, "Tracking/" .. key) end -- Normalize the key by treating it as a subpage of "Tracking", which gives -- the normalized title without the initial "Tracking/". If the normalized -- form has been memoized, don't transclude the page again. Otherwise, -- transclude the page using the `content` key on the title, which is the -- cheapest way to trigger transclusion, as it avoids any parser expansion -- of the target page. local normalized = sub(title.text, 10) -- excludes "Tracking/" if not memo[normalized] then title = title.content memo[normalized] = true end -- Memoize the original key. memo[key] = true end return function(input) if input == nil then error("No tracking key supplied to [[Module:debug/track]].", 2) elseif type(input) ~= "table" then track(input) return true end local key = input[1] if key == nil then error("No tracking keys in table supplied to [[Module:debug/track]].", 2) end local i = 1 repeat track(key) i = i + 1 key = input[i] until key == nil return true end p6sdr83482xvakdb92gtqfoyor6nnlb Module:title/makeTitle 828 7974 54709 2026-09-25T17:49:38Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local make_title = mw.title.makeTitle --[==[ A modified version of {mw.title.makeTitle} that fixes a bug which causes certain inputs to generate title objects for the empty string. Such empty string titles do not represent a valid page, and are broken in various ways (e.g. attempting to access certain keys results in an error); see [[phab:T240678]].]==] return function(ns, title, fragment, interwiki) local title = make_title(ns, title, fragment, interwiki) ret...' 54709 Scribunto text/plain local make_title = mw.title.makeTitle --[==[ A modified version of {mw.title.makeTitle} that fixes a bug which causes certain inputs to generate title objects for the empty string. Such empty string titles do not represent a valid page, and are broken in various ways (e.g. attempting to access certain keys results in an error); see [[phab:T240678]].]==] return function(ns, title, fragment, interwiki) local title = make_title(ns, title, fragment, interwiki) return title and title.prefixedText ~= "" and title or nil end coce82xngappecrexg1r1dsnope2mef Bysen:cln 10 7975 54714 2026-09-25T18:01:16Z Deadend0914 7211 Redirected page to [[Bysen:catlangname]] 54714 wikitext text/x-wiki #redirect [[Bysen:catlangname]] t38fq7v8cbypag95sf7ud405afbyh9t Module:utilities/data 828 7976 54715 2026-09-25T18:02:16Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local data = {} -- Namespaces which format_categories will allow: (main), Appendix, Thesaurus, Citations, Reconstruction data.category_namespaces = require("Module:table").listToSet{0, 100, 110, 114, 118} -- Script that should be applied to links in categories. data.catfix_scripts = { ["ab"] = "Cyrl", ["ae"] = "Avst", ["ar"] = "Arab", ["ary"] = "Arab", ["be"] = "Cyrl", ["cmn"] = "Hani", ["cu"] = "Cyrs", ["de"] = "Latn", ["el"] = "Grek", ["en"] = "Latn...' 54715 Scribunto text/plain local data = {} -- Namespaces which format_categories will allow: (main), Appendix, Thesaurus, Citations, Reconstruction data.category_namespaces = require("Module:table").listToSet{0, 100, 110, 114, 118} -- Script that should be applied to links in categories. data.catfix_scripts = { ["ab"] = "Cyrl", ["ae"] = "Avst", ["ar"] = "Arab", ["ary"] = "Arab", ["be"] = "Cyrl", ["cmn"] = "Hani", ["cu"] = "Cyrs", ["de"] = "Latn", ["el"] = "Grek", ["en"] = "Latn", ["fa"] = "Arab", ["grc"] = "Polyt", ["he"] = "Hebr", ["hi"] = "Deva", ["hu"] = "Latn", ["ja"] = "Jpan", ["ka"] = "Geor", ["ko"] = "Kore", ["mr"] = "Deva", ["nan-hbl"] = "Hani", ["orv"] = "Cyrs", ["ro"] = "Latn", ["ru"] = "Cyrl", ["sa"] = "Deva", ["sq"] = "Latn", ["syl"] = "Sylo", ["xfa"] = "Ital", ["yue"] = "Hani", ["zh"] = "Hani", ["zle-ono"] = "Cyrs", ["zle-ort"] = "Cyrs", --[[ [""] = "", ]] } return data 5r3vjfz59oyil3lm07bfrszeq7w30yp Module:headword/data 828 7977 54716 2026-09-25T18:03:02Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local headword_page_module = "Module:headword/page" local list_to_set = require("Module:table").listToSet local data = {} ------ 1. Lists which are converted into sets. ------ --[==[ var: Large pages where we disable label tracking, red link checking and similar. ]==] data.large_pages = list_to_set { -- pages that consistently hit timeouts "a", -- pages that sometimes hit timeouts "A", "baba", "de", "e", "i", "lima", "o", "u", "и", "山", "子"...' 54716 Scribunto text/plain local headword_page_module = "Module:headword/page" local list_to_set = require("Module:table").listToSet local data = {} ------ 1. Lists which are converted into sets. ------ --[==[ var: Large pages where we disable label tracking, red link checking and similar. ]==] data.large_pages = list_to_set { -- pages that consistently hit timeouts "a", -- pages that sometimes hit timeouts "A", "baba", "de", "e", "i", "lima", "o", "u", "и", "山", "子", "月", "一", "人", } --[==[ var: Map from singular to plural, and from plural to itself, for recognized parts of speech with irregular plurals. Most of these are invariable plurals, e.g. `kanji` is its own plural; but we also have `mora` plural `morae`. ]==] data.irregular_plurals = list_to_set({ "cmavo", "cmene", "fu'ivla", "gismu", "Han tu", "hanja", "hanzi", "jyutping", "kana", "kanji", "lujvo", "phrasebook", "pinyin", "rafsi", }, function(_, item) return item end) local irregular_plurals = data.irregular_plurals -- Irregular non-zero plurals AND any regular plurals where the singular ends in "s", -- because the module assumes that inputs ending in "s" are plurals. The singular and -- plural both need to be added, as the module will generate a default plural if -- the input doesn't match a key in this table. for sg, pl in next, { mora = "morae" } do irregular_plurals[sg], irregular_plurals[pl] = pl, pl end --[==[ var: Recognized lemmas. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the category 'LANG lemmas' will automatically be added. If the part of speech is not a singular or plural lemma or non-lemma form and is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added to various tracking categories: * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANG]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS/LANG]] ]==] data.lemmas = list_to_set{ "abbreviations", "acronyms", "adjectives", "adnominals", "adpositions", "adverbs", "affixes", "ambipositions", "articles", "circumfixes", "circumpositions", "classifiers", "cmavo", "cmavo clusters", "cmene", "combining forms", "conjunctions", "counters", "determiners", "diacritical marks", "digraphs", "equative adjectives", "fu'ivla", "gismu", "Han characters", "Han tu", "hanja", "hanzi", "ideophones", "idioms", "infixes", "initialisms", "iteration marks", "interfixes", "interjections", "kana", "kanji", "letters", "ligatures", "logograms", "lujvo", "morae", "morphemes", "non-constituents", "nouns", "numbers", "numeral symbols", "numerals", "particles", "phrases", "postpositions", "postpositional phrases", "predicatives", "prefixes", "prepositional phrases", "prepositions", "preverbs", "pronominal adverbs", "pronouns", "proper nouns", "proverbs", "punctuation marks", "relatives", "roots", "stems", "suffixes", "syllables", "symbols", "verbs", } --[==[ var: Recognized non-lemma forms. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the category 'LANG non-lemma forms' will automatically be added. If the part of speech is not a singular or plural lemma or non-lemma form and is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added to various tracking categories; see the documentation of `data.lemmas`. ]==] data.nonlemmas = list_to_set{ "active participle forms", "active participles", "adjectival participles", "adjective case forms", "adjective forms", "adjective feminine forms", "adjective plural forms", "adverb forms", "adverbial participles", "agent participles", "article forms", "circumfix forms", "combined forms", "comparative adjective forms", "comparative adjectives", "comparative adverb forms", "comparative adverbs", "conjunction forms", "contractions", "converbs", "determiner comparative forms", "determiner forms", "determiner superlative forms", "diminutive nouns", "elative adjectives", "equative adjective forms", "equative adjectives", "future participles", "gerunds", "infinitive forms", "infinitives", "interjection forms", "jyutping", "misspellings", "negative participles", "nominal participles", "noun case forms", "noun construct forms", "noun dual forms", "noun forms", "noun paucal forms", "noun plural forms", "noun possessive forms", "noun singulative forms", "numeral forms", "participles", "participle forms", "particle forms", "passive participles", "past active participles", "past adverbial participles", "past participles", "past participle forms", "past passive participles", "perfect active participles", "perfect participles", "perfect passive participles", "pinyin", "plurals", "postposition forms", "prefix forms", "preposition contractions", "preposition forms", "prepositional pronouns", "present active participles", "present adverbial participles", "present participles", "present passive participles", "preverb forms", "pronoun forms", "pronoun possessive forms", "proper noun forms", "proper noun plural forms", "rafsi", "romanizations", "root forms", "singulatives", "suffix forms", "superlative adjective forms", "superlative adjectives", "superlative adverb forms", "superlative adverbs", "verb forms", "verbal nouns", } --[==[ var: List of languages that will not have links to separate parts of the headword. ]==] data.no_multiword_links = list_to_set{ "zh", } --[==[ var: List of languages that will not have `LANG multiword terms` categories added. There are various reasons why languages are in this list: (a) words are written without spaces between them; (b) syllables are written with spaces between them; (c) variant reconstructions are notated with a tilde surrounded by spaces; (d) the language is a sign language, where pagenames are multiword descriptions of the gesture(s) required to make an individual sign; (e) some other weirdnesses. ]==] data.no_multiword_cat = list_to_set{ -------- Languages without spaces between words (sometimes spaces between phrases) -------- "blt", -- Tai Dam "ja", -- Japanese "khb", -- Lü "km", -- Khmer "lo", -- Lao "mnw", -- Mon "my", -- Burmese "nan", -- Min Nan (some words in Latin script; hyphens between syllables) "nan-hbl", -- Hokkien (some words in Latin script; hyphens between syllables) "nod", -- Northern Thai "ojp", -- Old Japanese "shn", -- Shan "sou", -- Southern Thai "tdd", -- Tai Nüa "th", -- Thai "tts", -- Isan "twh", -- Tai Dón "txg", -- Tangut "zh", -- Chinese (all varieties with Chinese characters) "zkt", -- Khitan -------- Languages with spaces between syllables -------- "ahk", -- Akha "aou", -- A'ou "atb", -- Zaiwa "byk", -- Biao "cdy", -- Chadong --"duu", -- Drung; not sure --"hmx-pro", -- Proto-Hmong-Mien --"hnj", -- Green Hmong; not sure "huq", -- Tsat "ium", -- Iu Mien --"lis", -- Lisu; not sure "mtq", -- Muong --"mww", -- White Hmong; not sure "onb", -- Lingao --"sit-gkh", -- Gokhy; not sure --"swi", -- Sui; not sure "tbq-lol-pro", -- Proto-Loloish "tdh", -- Thulung "ukk", -- Muak Sa-aak "vi", -- Vietnamese "yig", -- Wusa Nasu "zng", -- Mang -------- Languages with ~ with surrounding spaces used to separate variants -------- "mkh-ban-pro", -- Proto-Bahnaric "sit-pro", -- Proto-Sino-Tibetan; listed above -------- Other weirdnesses -------- "mul", -- Translingual; gestures, Morse code, etc. "aot", -- Atong (India); bullet is a letter -------- All sign languages -------- "ads", "aed", "aen", "afg", "ase", "asf", "asp", "asq", "asw", "bfi", "bfk", "bog", "bqn", "bqy", "bvl", "bzs", "cds", "csc", "csd", "cse", "csf", "csg", "csl", "csn", "csq", "csr", "doq", "dse", "dsl", "ecs", "esl", "esn", "eso", "eth", "fcs", "fse", "fsl", "fss", "gds", "gse", "gsg", "gsm", "gss", "gus", "hab", "haf", "hds", "hks", "hos", "hps", "hsh", "hsl", "icl", "iks", "ils", "inl", "ins", "ise", "isg", "isr", "jcs", "jhs", "jls", "jos", "jsl", "jus", "kgi", "kvk", "lbs", "lls", "lsl", "lso", "lsp", "lst", "lsy", "lws", "mdl", "mfs", "mre", "msd", "msr", "mzc", "mzg", "mzy", "nbs", "ncs", "nsi", "nsl", "nsp", "nsr", "nzs", "okl", "pgz", "pks", "prl", "prz", "psc", "psd", "psg", "psl", "pso", "psp", "psr", "pys", "rms", "rsl", "rsm", "sdl", "sfb", "sfs", "sgg", "sgx", "slf", "sls", "sqk", "sqs", "ssp", "ssr", "svk", "swl", "syy", "tse", "tsm", "tsq", "tss", "tsy", "tza", "ugn", "ugy", "ukl", "uks", "vgt", "vsi", "vsl", "vsv", "xki", "xml", "xms", "ygs", "ysl", "zib", "zsl", } --[==[ var: List of languages where a hyphen is not considered a word separator for the `LANG multiword terms` category. There are numerous reasons why languages are in this list; by each language should be listed the reason for inclusion. ]==] data.hyphen_not_multiword_sep = list_to_set{ "akk", -- Akkadian; hyphens between syllables "akl", -- Aklanon; hyphens for mid-word glottal stops "ber-pro", -- Proto-Berber; morphemes separated by hyphens "ceb", -- Cebuano; hyphens for mid-word glottal stops "cnk", -- Khumi Chin; hyphens used in single words "cpi", -- Chinese Pidgin English; Chinese-derived words with hyphens between syllables "de", -- German; too many false positives "esx-esk-pro", -- hyphen used to separate morphemes "fi", -- Finnish; hyphen used to separate components in compound words if the final and initial vowels match, respectively "gd", -- Scottish Gaelic; too many false positives like [[a-chianaibh]], [[a-nìos]], [[an-dè]] and other adverbs in a- and an- "hil", -- Hiligaynon; hyphens for mid-word glottal stops "hnn", -- Hanunoo; too many false positives "ilo", -- Ilocano; hyphens for mid-word glottal stops "kne", -- Kankanaey; hyphens for mid-word glottal stops "lcp", -- Western Lawa; dash as syllable joiner "lwl", -- Eastern Lawa; dash as syllable joiner "mfa", -- Pattani Malay in Thai script; dash as syllable joiner "mkh-vie-pro", -- Proto-Vietic; morphemes separated by hyphens "msb", -- Masbatenyo; too many false positives "tl", -- Tagalog; too many false positives "war", -- Waray-Waray; too many false positives "yo", -- Yoruba; hyphens used to show lengthened nasal vowels } --[==[ var: List of languages that will not have `LANG masculine nouns` and similar categories added. Generally, these languages are lacking gender but use the gender field for other purposes. (This is a massive hack and should be changed.) ]==] data.no_gender_cat = list_to_set{ -- Languages without gender but which use the gender field for other purposes "ja", "th", } --[==[ var: List of languages where [[Module:headword]] should not attempt to generate a transliteration even if the term is written in a non-Latin script. FIXME: Notate reasons why each language is in this list. ]==] data.notranslit = list_to_set{ "ams", "az", "bbc", "bug", "cdo", "cia", "cjm", "cjy", "cmn", "cnp", "cpi", "cpx", "csp", "czh", "czo", "gan", "hak", "hnm", "hsn", "ja", "kzg", "lad", "ltc", "luh", "lzh", "mnp", "ms", "mul", "mvi", "nan", "nan-dat", "nan-hbl", "nan-hlh", "nan-lnx", "nan-tws", "nan-zhe", "nan-zsh", "och", "oj", "okn", "ryn", "rys", "ryu", "sh", "sjc", "tgt", "th", "tkn", "tly", "txg", "und", "vi", "wuu", "xug", "yoi", "yox", "yue", "za", "zh", "zhx-sic", "zhx-tai", } --[==[ var: Languages where we track all direct uses of {{tl|head}} in place of a language-specific template, such as {{tl|en-head}}, {{tl|hi-head}}, {{tl|sa-head}}, {{tl|tl-head}}. ]==] data.track_head_template = list_to_set{ -- "en", -- when the time comes? "hi", "sa", "tl", } --[==[ var: List of script codes for which a script-tagged display title will be added. ]==] data.toBeTagged = list_to_set{ "Ahom", "Arab", "fa-Arab", "Aran", "Armi", "Armn", "Avst", "Bali", "Bamu", "Batk", "Beng", "as-Beng", "Bopo", "Brah", "Brai", "Bugi", "Buhd", "Cakm", "Cans", "Cari", "Cham", "Cher", "Copt", "Cprt", "Cyrl", "Cyrs", "Deva", "Dsrt", "Egyd", "Egyp", "Ethi", "Geok", "Geor", "Glag", "Goth", "Grek", "Polyt", "Gujr", "Guru", "Hang", "Hani", "Hano", "Hebr", "Hira", "Hluw", "Ital", "Java", "Kali", "Kana", "Khar", "Khmr", "Knda", "Kthi", "Lana", "Laoo", "Latn", "Latf", "Latg", "pjt-Latn", "Lepc", "Limb", "Linb", "Lisu", "Lyci", "Lydi", "Mand", "Mani", "Marc", "Merc", "Mero", "Mlym", "Mong", "mnc-Mong", "sjo-Mong", "xwo-Mong", "Mtei", "Mymr", "Narb", "Nkoo", "Nshu", "Ogam", "Olck", "Orkh", "Orya", "Osma", "Ougr", "Palm", "Phag", "Phli", "Phlv", "Phnx", "Plrd", "Prti", "Rjng", "Runr", "Samr", "Sarb", "Saur", "Sgnw", "Shaw", "Shrd", "Sinh", "Sora", "Sund", "Sylo", "Syrc", "Tagb", "Tale", "Talu", "Taml", "Tang", "Tavt", "Telu", "Tfng", "Tglg", "Thaa", "Thai", "Tibt", "Ugar", "Vaii", "Xpeo", "Xsux", "Yiii", "Zmth", "Zsym", "Ipach", "Music", "Rumin", } --[==[ var: Parts of speech which will not be categorised in categories like `English terms spelled with É` if the term is the character in question (e.g. the letter entry for English [[é]]). This contrasts with entries like the French adjective [[m̂]], which is a one-letter word spelled with the letter. ]==] data.pos_not_spelled_with_self = list_to_set{ "diacritical marks", "Han characters", "Han tu", "hanja", "hanzi", "iteration marks", "kana", "kanji", "letters", "ligatures", "logograms", "morae", "numeral symbols", "numerals", "punctuation marks", "syllables", "symbols", } ------ 2. Lists not converted into sets. ------ --[==[ var: List of languages that will default to `sccat` being true, i.e. categories like `LANG POS in SCRIPT script` will automatically be generated. This can be overridden using {{para|sccat|0}} in {{tl|head}} or setting `sccat` to {false} in Lua. If the value on the right-hand side is {true}, such categories will be generated for all scripts. If {false}, no categories will be generated (this is useful for etym languages that override the spec of their parent). If a list of scripts, such categories will be generated only for the specified scripts. If a list of scripts where the first element is {"not"}, such categories will be generated for all scripts ''except'' the specified scripts. ]==] data.default_sccat = { ["af"] = {"not", "Latn"}, ["az"] = {"not", "Latn"}, ["inc-apa"] = true, ["inc-ash"] = true, ["kfr"] = true, ["ks"] = true, ["mr"] = true, ["mwr"] = true, ["inc-oaw"] = true, ["inc-ohi"] = true, ["omr"] = true, ["inc-opa"] = true, ["phr"] = true, ["pi"] = true, ["pra"] = true, ["sa"] = true, ["skr"] = true, ["sd"] = true, ["yi"] = {"not", "Hebr"}, } --[==[ var: Recognized aliases for parts of speech (param 2=). Key is the short form and value is the canonical singular (not pluralized) form. It is singular so the same table can be used in [[Module:form of]] for the {{para|p}}/{{para|POS}} param and [[Module:links]] for the pos= param. Note that any part of speech, abbreviated or not, can be suffixed with `f` to generate the corresponding non-lemma form part of speech, such as `adjf`, `af` or `adjectivef` for `adjective form`, and `nounf` or `nf` for `noun form`. This expansion happens even when it does not make sense for the given part of speech (e.g. `pclf` expands to `particle form` and `symf` expands to `symbol form`), and currently also, at least in [[Module:headword]] (but not [[Module:links]]), even if the part before the `f` is not a recognized part of speech or abbreviation (hence `nerf` expands to `ner form`). ]==] data.pos_aliases = { a = "adjective", adj = "adjective", adv = "adverb", art = "article", aug = "augmentative", cls = "classifier", compadj = "comparative adjective", compadv = "comparative adverb", compdet = "comparative determiner", comppron = "comparative pronoun", conj = "conjunction", contr = "contraction", conv = "converb", det = "determiner", dim = "diminutive", infin = "infinitive", -- not inf, which is ambiguous due to infixes int = "interjection", interj = "interjection", intj = "interjection", n = "noun", -- the next two support Algonquian languages; see also vii/vai/vti/vta below na = "animate noun", ni = "inanimate noun", num = "numeral", part = "participle", pastpart = "past participle", pastptcp = "past participle", pcl = "particle", phr = "phrase", pn = "proper noun", postp = "postposition", pref = "prefix", prep = "preposition", prepphr = "prepositional phrase", prespart = "present participle", presptcp = "present participle", pron = "pronoun", prop = "proper noun", proper = "proper noun", propn = "proper noun", ptcp = "participle", rom = "romanization", roman = "romanization", romanisation = "romanization", romanisations = "romanization", suf = "suffix", supadj = "superlative adjective", supadv = "superlative adverb", supdet = "superlative determiner", suppron = "superlative pronoun", sym = "symbol", v = "verb", vb = "verb", vi = "intransitive verb", vm = "modal verb", vt = "transitive verb", -- the next four support Algonquian languages vii = "inanimate intransitive verb", vai = "animate intransitive verb", vti = "transitive inanimate verb", vta = "transitive animate verb", } --[==[ var: Map of parts of speech for which categories like `German masculine nouns` or `Russian imperfective verbs` will be generated if the headword is of the appropriate gender/number. The map is used to canonicalize parts of speech for categorization purposes; specifically, proper nouns categorizes like nouns. ]==] data.pos_for_gender_number_cat = { ["nouns"] = "nouns", ["proper nouns"] = "nouns", ["suffixes"] = "suffixes", -- We include verbs because impf and pf are valid "genders". ["verbs"] = "verbs", } --[==[ var: Lower limit for a "long" word in a particular language. Used to categorize terms into e.g. [[:Category:Long English words]] automatically. Languages with no mapping here do not get categorized. ]==] data.long_word_thresholds = { ["af"] = 20, ["bg"] = 20, ["cy"] = 25, ["de"] = 20, ["en"] = 25, ["es"] = 20, ["fr"] = 20, ["ka"] = 20, ["sv"] = 20, ["tl"] = 25, } ------ 3. Page-wide processing (so that it only needs to be done once per page). ------ data.page = require(headword_page_module).process_page() -- Set some page properties directly on `data` for ease of use. data.pagename = data.page.pagename data.encoded_pagename = data.page.encoded_pagename return data t7hvmyti6uhkzovx0miht4z0rse3rxl Module:headword/page 828 7978 54717 2026-09-25T18:03:44Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local languages_module = "Module:languages" local maintenance_category_module = "Module:maintenance category" local pages_module = "Module:pages" local string_compare_module = "Module:string/compare" local string_decode_entities_module = "Module:string/decodeEntities" local string_remove_comments_module = "Module:string/removeComments" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_p...' 54717 Scribunto text/plain local export = {} local languages_module = "Module:languages" local maintenance_category_module = "Module:maintenance category" local pages_module = "Module:pages" local string_compare_module = "Module:string/compare" local string_decode_entities_module = "Module:string/decodeEntities" local string_remove_comments_module = "Module:string/removeComments" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local mw = mw local string = string local table = table local ustring = mw.ustring local concat = table.concat local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local load_data = mw.loadData local match = string.match local new_title = mw.title.new local pairs = pairs local require = require local sub = string.sub local toNFC = ustring.toNFC local toNFD = ustring.toNFD local ugsub = ustring.gsub local function class_else_type(...) class_else_type = require(template_parser_module).class_else_type return class_else_type(...) end local function decode_entities(...) decode_entities = require(string_decode_entities_module) return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function get_category(...) get_category = require(maintenance_category_module).get_category return get_category(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function parse(...) parse = require(template_parser_module).parse return parse(...) end local function remove_comments(...) remove_comments = require(string_remove_comments_module) return remove_comments(...) end local function physical_to_logical_pagename_if_mammoth(...) physical_to_logical_pagename_if_mammoth = require(pages_module).physical_to_logical_pagename_if_mammoth return physical_to_logical_pagename_if_mammoth(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function string_compare(...) string_compare = require(string_compare_module) return string_compare(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local langnames local function get_langnames() langnames, get_langnames = load_data("Module:languages/canonical names"), nil return langnames end -- Combining character data used when categorising unusual characters. These resolve into two patterns, used to find -- single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + -- diacritic(s) + character). -- Charsets are in the format used by Unicode's UnicodeSet tool: https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp. -- Single combining characters. -- Charset: [[:M:]&[:^Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] -- Note: concatenating hundreds of lines at once gives an error, so () are used every 150 lines to break it up into chunks. local comb_chars_single = ("\204\128-\205\142" .. -- U+0300-U+034E "\205\144-\205\155" .. -- U+0350-U+035B "\205\163-\205\175" .. -- U+0363-U+036F "\210\131-\210\137" .. -- U+0483-U+0489 "\214\145-\214\189" .. -- U+0591-U+05BD "\214\191" .. -- U+05BF "\215\129" .. -- U+05C1 "\215\130" .. -- U+05C2 "\215\132" .. -- U+05C4 "\215\133" .. -- U+05C5 "\215\135" .. -- U+05C7 "\216\144-\216\154" .. -- U+0610-U+061A "\217\139-\217\159" .. -- U+064B-U+065F "\217\176" .. -- U+0670 "\219\150-\219\156" .. -- U+06D6-U+06DC "\219\159-\219\164" .. -- U+06DF-U+06E4 "\219\167" .. -- U+06E7 "\219\168" .. -- U+06E8 "\219\170-\219\173" .. -- U+06EA-U+06ED "\220\145" .. -- U+0711 "\220\176-\221\138" .. -- U+0730-U+074A "\222\166-\222\176" .. -- U+07A6-U+07B0 "\223\171-\223\179" .. -- U+07EB-U+07F3 "\223\189" .. -- U+07FD "\224\160\150-\224\160\153" .. -- U+0816-U+0819 "\224\160\155-\224\160\163" .. -- U+081B-U+0823 "\224\160\165-\224\160\167" .. -- U+0825-U+0827 "\224\160\169-\224\160\173" .. -- U+0829-U+082D "\224\161\153-\224\161\155" .. -- U+0859-U+085B "\224\162\151-\224\162\159" .. -- U+0897-U+089F "\224\163\138-\224\163\161" .. -- U+08CA-U+08E1 "\224\163\163-\224\164\131" .. -- U+08E3-U+0903 "\224\164\186-\224\164\188" .. -- U+093A-U+093C "\224\164\190-\224\165\143" .. -- U+093E-U+094F "\224\165\145-\224\165\151" .. -- U+0951-U+0957 "\224\165\162" .. -- U+0962 "\224\165\163" .. -- U+0963 "\224\166\129-\224\166\131" .. -- U+0981-U+0983 "\224\166\188" .. -- U+09BC "\224\166\190-\224\167\132" .. -- U+09BE-U+09C4 "\224\167\135" .. -- U+09C7 "\224\167\136" .. -- U+09C8 "\224\167\139-\224\167\141" .. -- U+09CB-U+09CD "\224\167\151" .. -- U+09D7 "\224\167\162" .. -- U+09E2 "\224\167\163" .. -- U+09E3 "\224\167\190" .. -- U+09FE "\224\168\129-\224\168\131" .. -- U+0A01-U+0A03 "\224\168\188" .. -- U+0A3C "\224\168\190-\224\169\130" .. -- U+0A3E-U+0A42 "\224\169\135" .. -- U+0A47 "\224\169\136" .. -- U+0A48 "\224\169\139-\224\169\141" .. -- U+0A4B-U+0A4D "\224\169\145" .. -- U+0A51 "\224\169\176" .. -- U+0A70 "\224\169\177" .. -- U+0A71 "\224\169\181" .. -- U+0A75 "\224\170\129-\224\170\131" .. -- U+0A81-U+0A83 "\224\170\188" .. -- U+0ABC "\224\170\190-\224\171\133" .. -- U+0ABE-U+0AC5 "\224\171\135-\224\171\137" .. -- U+0AC7-U+0AC9 "\224\171\139-\224\171\141" .. -- U+0ACB-U+0ACD "\224\171\162" .. -- U+0AE2 "\224\171\163" .. -- U+0AE3 "\224\171\186-\224\171\191" .. -- U+0AFA-U+0AFF "\224\172\129-\224\172\131" .. -- U+0B01-U+0B03 "\224\172\188" .. -- U+0B3C "\224\172\190-\224\173\132" .. -- U+0B3E-U+0B44 "\224\173\135" .. -- U+0B47 "\224\173\136" .. -- U+0B48 "\224\173\139-\224\173\141" .. -- U+0B4B-U+0B4D "\224\173\149-\224\173\151" .. -- U+0B55-U+0B57 "\224\173\162" .. -- U+0B62 "\224\173\163" .. -- U+0B63 "\224\174\130" .. -- U+0B82 "\224\174\190-\224\175\130" .. -- U+0BBE-U+0BC2 "\224\175\134-\224\175\136" .. -- U+0BC6-U+0BC8 "\224\175\138-\224\175\141" .. -- U+0BCA-U+0BCD "\224\175\151" .. -- U+0BD7 "\224\176\128-\224\176\132" .. -- U+0C00-U+0C04 "\224\176\188" .. -- U+0C3C "\224\176\190-\224\177\132" .. -- U+0C3E-U+0C44 "\224\177\134-\224\177\136" .. -- U+0C46-U+0C48 "\224\177\138-\224\177\141" .. -- U+0C4A-U+0C4D "\224\177\149" .. -- U+0C55 "\224\177\150" .. -- U+0C56 "\224\177\162" .. -- U+0C62 "\224\177\163" .. -- U+0C63 "\224\178\129-\224\178\131" .. -- U+0C81-U+0C83 "\224\178\188" .. -- U+0CBC "\224\178\190-\224\179\132" .. -- U+0CBE-U+0CC4 "\224\179\134-\224\179\136" .. -- U+0CC6-U+0CC8 "\224\179\138-\224\179\141" .. -- U+0CCA-U+0CCD "\224\179\149" .. -- U+0CD5 "\224\179\150" .. -- U+0CD6 "\224\179\162" .. -- U+0CE2 "\224\179\163" .. -- U+0CE3 "\224\179\179" .. -- U+0CF3 "\224\180\128-\224\180\131" .. -- U+0D00-U+0D03 "\224\180\187" .. -- U+0D3B "\224\180\188" .. -- U+0D3C "\224\180\190-\224\181\132" .. -- U+0D3E-U+0D44 "\224\181\134-\224\181\136" .. -- U+0D46-U+0D48 "\224\181\138-\224\181\141" .. -- U+0D4A-U+0D4D "\224\181\151" .. -- U+0D57 "\224\181\162" .. -- U+0D62 "\224\181\163" .. -- U+0D63 "\224\182\129-\224\182\131" .. -- U+0D81-U+0D83 "\224\183\138" .. -- U+0DCA "\224\183\143-\224\183\148" .. -- U+0DCF-U+0DD4 "\224\183\150" .. -- U+0DD6 "\224\183\152-\224\183\159" .. -- U+0DD8-U+0DDF "\224\183\178" .. -- U+0DF2 "\224\183\179" .. -- U+0DF3 "\224\184\177" .. -- U+0E31 "\224\184\180-\224\184\186" .. -- U+0E34-U+0E3A "\224\185\135-\224\185\142" .. -- U+0E47-U+0E4E "\224\186\177" .. -- U+0EB1 "\224\186\180-\224\186\188" .. -- U+0EB4-U+0EBC "\224\187\136-\224\187\142" .. -- U+0EC8-U+0ECE "\224\188\152" .. -- U+0F18 "\224\188\153" .. -- U+0F19 "\224\188\181" .. -- U+0F35 "\224\188\183" .. -- U+0F37 "\224\188\185" .. -- U+0F39 "\224\188\190" .. -- U+0F3E "\224\188\191" .. -- U+0F3F "\224\189\177-\224\190\132" .. -- U+0F71-U+0F84 "\224\190\134" .. -- U+0F86 "\224\190\135" .. -- U+0F87 "\224\190\141-\224\190\151" .. -- U+0F8D-U+0F97 "\224\190\153-\224\190\188" .. -- U+0F99-U+0FBC "\224\191\134" .. -- U+0FC6 "\225\128\171-\225\128\190" .. -- U+102B-U+103E "\225\129\150-\225\129\153" .. -- U+1056-U+1059 "\225\129\158-\225\129\160" .. -- U+105E-U+1060 "\225\129\162-\225\129\164" .. -- U+1062-U+1064 "\225\129\167-\225\129\173" .. -- U+1067-U+106D "\225\129\177-\225\129\180" .. -- U+1071-U+1074 "\225\130\130-\225\130\141" .. -- U+1082-U+108D "\225\130\143" .. -- U+108F "\225\130\154-\225\130\157" .. -- U+109A-U+109D "\225\141\157-\225\141\159" .. -- U+135D-U+135F "\225\156\146-\225\156\149" .. -- U+1712-U+1715 "\225\156\178-\225\156\180" .. -- U+1732-U+1734 "\225\157\146" .. -- U+1752 "\225\157\147" .. -- U+1753 "\225\157\178" .. -- U+1772 "\225\157\179" .. -- U+1773 "\225\158\180-\225\159\147") .. -- U+17B4-U+17D3 ("\225\159\157" .. -- U+17DD "\225\162\133" .. -- U+1885 "\225\162\134" .. -- U+1886 "\225\162\169" .. -- U+18A9 "\225\164\160-\225\164\171" .. -- U+1920-U+192B "\225\164\176-\225\164\187" .. -- U+1930-U+193B "\225\168\151-\225\168\155" .. -- U+1A17-U+1A1B "\225\169\149-\225\169\158" .. -- U+1A55-U+1A5E "\225\169\160-\225\169\188" .. -- U+1A60-U+1A7C "\225\169\191" .. -- U+1A7F "\225\170\176-\225\171\142" .. -- U+1AB0-U+1ACE "\225\172\128-\225\172\132" .. -- U+1B00-U+1B04 "\225\172\180-\225\173\132" .. -- U+1B34-U+1B44 "\225\173\171-\225\173\179" .. -- U+1B6B-U+1B73 "\225\174\128-\225\174\130" .. -- U+1B80-U+1B82 "\225\174\161-\225\174\173" .. -- U+1BA1-U+1BAD "\225\175\166-\225\175\179" .. -- U+1BE6-U+1BF3 "\225\176\164-\225\176\183" .. -- U+1C24-U+1C37 "\225\179\144-\225\179\146" .. -- U+1CD0-U+1CD2 "\225\179\148-\225\179\168" .. -- U+1CD4-U+1CE8 "\225\179\173" .. -- U+1CED "\225\179\180" .. -- U+1CF4 "\225\179\183-\225\179\185" .. -- U+1CF7-U+1CF9 "\225\183\128-\225\183\140" .. -- U+1DC0-U+1DCC "\225\183\142-\225\183\187" .. -- U+1DCE-U+1DFB "\225\183\189-\225\183\191" .. -- U+1DFD-U+1DFF "\226\131\144-\226\131\176" .. -- U+20D0-U+20F0 "\226\179\175-\226\179\177" .. -- U+2CEF-U+2CF1 "\226\181\191" .. -- U+2D7F "\226\183\160-\226\183\191" .. -- U+2DE0-U+2DFF "\227\128\170-\227\128\175" .. -- U+302A-U+302F "\227\130\153" .. -- U+3099 "\227\130\154" .. -- U+309A "\234\153\175-\234\153\178" .. -- U+A66F-U+A672 "\234\153\180-\234\153\189" .. -- U+A674-U+A67D "\234\154\158" .. -- U+A69E "\234\154\159" .. -- U+A69F "\234\155\176" .. -- U+A6F0 "\234\155\177" .. -- U+A6F1 "\234\160\130" .. -- U+A802 "\234\160\134" .. -- U+A806 "\234\160\139" .. -- U+A80B "\234\160\163-\234\160\167" .. -- U+A823-U+A827 "\234\160\172" .. -- U+A82C "\234\162\128" .. -- U+A880 "\234\162\129" .. -- U+A881 "\234\162\180-\234\163\133" .. -- U+A8B4-U+A8C5 "\234\163\160-\234\163\177" .. -- U+A8E0-U+A8F1 "\234\163\191" .. -- U+A8FF "\234\164\166-\234\164\173" .. -- U+A926-U+A92D "\234\165\135-\234\165\147" .. -- U+A947-U+A953 "\234\166\128-\234\166\131" .. -- U+A980-U+A983 "\234\166\179-\234\167\128" .. -- U+A9B3-U+A9C0 "\234\167\165" .. -- U+A9E5 "\234\168\169-\234\168\182" .. -- U+AA29-U+AA36 "\234\169\131" .. -- U+AA43 "\234\169\140" .. -- U+AA4C "\234\169\141" .. -- U+AA4D "\234\169\187-\234\169\189" .. -- U+AA7B-U+AA7D "\234\170\176" .. -- U+AAB0 "\234\170\178-\234\170\180" .. -- U+AAB2-U+AAB4 "\234\170\183" .. -- U+AAB7 "\234\170\184" .. -- U+AAB8 "\234\170\190" .. -- U+AABE "\234\170\191" .. -- U+AABF "\234\171\129" .. -- U+AAC1 "\234\171\171-\234\171\175" .. -- U+AAEB-U+AAEF "\234\171\181" .. -- U+AAF5 "\234\171\182" .. -- U+AAF6 "\234\175\163-\234\175\170" .. -- U+ABE3-U+ABEA "\234\175\172" .. -- U+ABEC "\234\175\173" .. -- U+ABED "\239\172\158" .. -- U+FB1E "\239\184\160-\239\184\175" .. -- U+FE20-U+FE2F "\240\144\135\189" .. -- U+101FD "\240\144\139\160" .. -- U+102E0 "\240\144\141\182-\240\144\141\186" .. -- U+10376-U+1037A "\240\144\168\129-\240\144\168\131" .. -- U+10A01-U+10A03 "\240\144\168\133" .. -- U+10A05 "\240\144\168\134" .. -- U+10A06 "\240\144\168\140-\240\144\168\143" .. -- U+10A0C-U+10A0F "\240\144\168\184-\240\144\168\186" .. -- U+10A38-U+10A3A "\240\144\168\191" .. -- U+10A3F "\240\144\171\165" .. -- U+10AE5 "\240\144\171\166" .. -- U+10AE6 "\240\144\180\164-\240\144\180\167" .. -- U+10D24-U+10D27 "\240\144\181\169-\240\144\181\173" .. -- U+10D69-U+10D6D "\240\144\186\171" .. -- U+10EAB "\240\144\186\172" .. -- U+10EAC "\240\144\187\188-\240\144\187\191" .. -- U+10EFC-U+10EFF "\240\144\189\134-\240\144\189\144" .. -- U+10F46-U+10F50 "\240\144\190\130-\240\144\190\133" .. -- U+10F82-U+10F85 "\240\145\128\128-\240\145\128\130" .. -- U+11000-U+11002 "\240\145\128\184-\240\145\129\134" .. -- U+11038-U+11046 "\240\145\129\176" .. -- U+11070 "\240\145\129\179" .. -- U+11073 "\240\145\129\180" .. -- U+11074 "\240\145\129\191-\240\145\130\130" .. -- U+1107F-U+11082 "\240\145\130\176-\240\145\130\186" .. -- U+110B0-U+110BA "\240\145\131\130" .. -- U+110C2 "\240\145\132\128-\240\145\132\130" .. -- U+11100-U+11102 "\240\145\132\167-\240\145\132\180" .. -- U+11127-U+11134 "\240\145\133\133" .. -- U+11145 "\240\145\133\134" .. -- U+11146 "\240\145\133\179" .. -- U+11173 "\240\145\134\128-\240\145\134\130" .. -- U+11180-U+11182 "\240\145\134\179-\240\145\135\128" .. -- U+111B3-U+111C0 "\240\145\135\137-\240\145\135\140" .. -- U+111C9-U+111CC "\240\145\135\142" .. -- U+111CE "\240\145\135\143" .. -- U+111CF "\240\145\136\172-\240\145\136\183" .. -- U+1122C-U+11237 "\240\145\136\190" .. -- U+1123E "\240\145\137\129" .. -- U+11241 "\240\145\139\159-\240\145\139\170" .. -- U+112DF-U+112EA "\240\145\140\128-\240\145\140\131" .. -- U+11300-U+11303 "\240\145\140\187" .. -- U+1133B "\240\145\140\188" .. -- U+1133C "\240\145\140\190-\240\145\141\132" .. -- U+1133E-U+11344 "\240\145\141\135" .. -- U+11347 "\240\145\141\136" .. -- U+11348 "\240\145\141\139-\240\145\141\141" .. -- U+1134B-U+1134D "\240\145\141\151" .. -- U+11357 "\240\145\141\162" .. -- U+11362 "\240\145\141\163" .. -- U+11363 "\240\145\141\166-\240\145\141\172" .. -- U+11366-U+1136C "\240\145\141\176-\240\145\141\180" .. -- U+11370-U+11374 "\240\145\142\184-\240\145\143\128" .. -- U+113B8-U+113C0 "\240\145\143\130" .. -- U+113C2 "\240\145\143\133" .. -- U+113C5 "\240\145\143\135-\240\145\143\138" .. -- U+113C7-U+113CA "\240\145\143\140-\240\145\143\144" .. -- U+113CC-U+113D0 "\240\145\143\146" .. -- U+113D2 "\240\145\143\161" .. -- U+113E1 "\240\145\143\162" .. -- U+113E2 "\240\145\144\181-\240\145\145\134" .. -- U+11435-U+11446 "\240\145\145\158" .. -- U+1145E "\240\145\146\176-\240\145\147\131" .. -- U+114B0-U+114C3 "\240\145\150\175-\240\145\150\181" .. -- U+115AF-U+115B5 "\240\145\150\184-\240\145\151\128" .. -- U+115B8-U+115C0 "\240\145\151\156" .. -- U+115DC "\240\145\151\157" .. -- U+115DD "\240\145\152\176-\240\145\153\128" .. -- U+11630-U+11640 "\240\145\154\171-\240\145\154\183" .. -- U+116AB-U+116B7 "\240\145\156\157-\240\145\156\171" .. -- U+1171D-U+1172B "\240\145\160\172-\240\145\160\186" .. -- U+1182C-U+1183A "\240\145\164\176-\240\145\164\181" .. -- U+11930-U+11935 "\240\145\164\183" .. -- U+11937 "\240\145\164\184" .. -- U+11938 "\240\145\164\187-\240\145\164\190" .. -- U+1193B-U+1193E "\240\145\165\128") .. -- U+11940 ("\240\145\165\130" .. -- U+11942 "\240\145\165\131" .. -- U+11943 "\240\145\167\145-\240\145\167\151" .. -- U+119D1-U+119D7 "\240\145\167\154-\240\145\167\160" .. -- U+119DA-U+119E0 "\240\145\167\164" .. -- U+119E4 "\240\145\168\129-\240\145\168\138" .. -- U+11A01-U+11A0A "\240\145\168\179-\240\145\168\185" .. -- U+11A33-U+11A39 "\240\145\168\187-\240\145\168\190" .. -- U+11A3B-U+11A3E "\240\145\169\135" .. -- U+11A47 "\240\145\169\145-\240\145\169\155" .. -- U+11A51-U+11A5B "\240\145\170\138-\240\145\170\153" .. -- U+11A8A-U+11A99 "\240\145\176\175-\240\145\176\182" .. -- U+11C2F-U+11C36 "\240\145\176\184-\240\145\176\191" .. -- U+11C38-U+11C3F "\240\145\178\146-\240\145\178\167" .. -- U+11C92-U+11CA7 "\240\145\178\169-\240\145\178\182" .. -- U+11CA9-U+11CB6 "\240\145\180\177-\240\145\180\182" .. -- U+11D31-U+11D36 "\240\145\180\186" .. -- U+11D3A "\240\145\180\188" .. -- U+11D3C "\240\145\180\189" .. -- U+11D3D "\240\145\180\191-\240\145\181\133" .. -- U+11D3F-U+11D45 "\240\145\181\135" .. -- U+11D47 "\240\145\182\138-\240\145\182\142" .. -- U+11D8A-U+11D8E "\240\145\182\144" .. -- U+11D90 "\240\145\182\145" .. -- U+11D91 "\240\145\182\147-\240\145\182\151" .. -- U+11D93-U+11D97 "\240\145\187\179-\240\145\187\182" .. -- U+11EF3-U+11EF6 "\240\145\188\128" .. -- U+11F00 "\240\145\188\129" .. -- U+11F01 "\240\145\188\131" .. -- U+11F03 "\240\145\188\180-\240\145\188\186" .. -- U+11F34-U+11F3A "\240\145\188\190-\240\145\189\130" .. -- U+11F3E-U+11F42 "\240\145\189\154" .. -- U+11F5A "\240\147\145\128" .. -- U+13440 "\240\147\145\135-\240\147\145\149" .. -- U+13447-U+13455 "\240\150\132\158-\240\150\132\175" .. -- U+1611E-U+1612F "\240\150\171\176-\240\150\171\180" .. -- U+16AF0-U+16AF4 "\240\150\172\176-\240\150\172\182" .. -- U+16B30-U+16B36 "\240\150\189\143" .. -- U+16F4F "\240\150\189\145-\240\150\190\135" .. -- U+16F51-U+16F87 "\240\150\190\143-\240\150\190\146" .. -- U+16F8F-U+16F92 "\240\150\191\164" .. -- U+16FE4 "\240\150\191\176" .. -- U+16FF0 "\240\150\191\177" .. -- U+16FF1 "\240\155\178\157" .. -- U+1BC9D "\240\155\178\158" .. -- U+1BC9E "\240\156\188\128-\240\156\188\173" .. -- U+1CF00-U+1CF2D "\240\156\188\176-\240\156\189\134" .. -- U+1CF30-U+1CF46 "\240\157\133\165-\240\157\133\169" .. -- U+1D165-U+1D169 "\240\157\133\173-\240\157\133\178" .. -- U+1D16D-U+1D172 "\240\157\133\187-\240\157\134\130" .. -- U+1D17B-U+1D182 "\240\157\134\133-\240\157\134\139" .. -- U+1D185-U+1D18B "\240\157\134\170-\240\157\134\173" .. -- U+1D1AA-U+1D1AD "\240\157\137\130-\240\157\137\132" .. -- U+1D242-U+1D244 "\240\157\168\128-\240\157\168\182" .. -- U+1DA00-U+1DA36 "\240\157\168\187-\240\157\169\172" .. -- U+1DA3B-U+1DA6C "\240\157\169\181" .. -- U+1DA75 "\240\157\170\132" .. -- U+1DA84 "\240\157\170\155-\240\157\170\159" .. -- U+1DA9B-U+1DA9F "\240\157\170\161-\240\157\170\175" .. -- U+1DAA1-U+1DAAF "\240\158\128\128-\240\158\128\134" .. -- U+1E000-U+1E006 "\240\158\128\136-\240\158\128\152" .. -- U+1E008-U+1E018 "\240\158\128\155-\240\158\128\161" .. -- U+1E01B-U+1E021 "\240\158\128\163" .. -- U+1E023 "\240\158\128\164" .. -- U+1E024 "\240\158\128\166-\240\158\128\170" .. -- U+1E026-U+1E02A "\240\158\130\143" .. -- U+1E08F "\240\158\132\176-\240\158\132\182" .. -- U+1E130-U+1E136 "\240\158\138\174" .. -- U+1E2AE "\240\158\139\172-\240\158\139\175" .. -- U+1E2EC-U+1E2EF "\240\158\147\172-\240\158\147\175" .. -- U+1E4EC-U+1E4EF "\240\158\151\174" .. -- U+1E5EE "\240\158\151\175" .. -- U+1E5EF "\240\158\163\144-\240\158\163\150" .. -- U+1E8D0-U+1E8D6 "\240\158\165\132-\240\158\165\138") -- U+1E944-U+1E94A -- Double combining characters. -- Charset: [[:M:]&[:Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] local comb_chars_double = "\205\156-\205\162" .. -- U+035C-U+0362 "\225\183\141" .. -- U+1DCD "\225\183\188" -- U+1DFC -- Variation selectors etc.; separated out so that we don't get categories for them. -- Charset: [[:M:]&[[:subhead=Grapheme joiner:][:Variation_Selector=Yes:]]]. local comb_chars_other = "\205\143" .. -- U+034F "\225\160\139-\225\160\141" .. -- U+180B-U+180D "\225\160\143" .. -- U+180F "\239\184\128-\239\184\143" .. -- U+FE00-U+FE0F "\243\160\132\128-\243\160\135\175" -- U+E0100-U+E01EF local comb_chars_all = comb_chars_single .. comb_chars_double .. comb_chars_other local comb_chars = { combined_single = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]+%f[^" .. comb_chars_all .. "]", combined_double = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]*[" .. comb_chars_double .. "]+[" .. comb_chars_all .. "]*.[" .. comb_chars_single .. comb_chars_other .. "]*", diacritics_single = "[" .. comb_chars_single .. "]", diacritics_double = "[" .. comb_chars_double .. "]", diacritics_all = "[" .. comb_chars_all .. "]" } -- Somewhat curated list from https://unicode.org/Public/emoji/16.0/emoji-sequences.txt. -- NOTE: There are lots more emoji sequences involving non-emoji Plane 0 symbols followed by 0xFE0F, which we don't -- (yet?) handle. local emoji_chars = "\226\140\154" .. -- U+231A (⌚) "\226\140\155" .. -- U+231B (⌛) "\226\140\168" .. -- U+2328 (⌨) "\226\143\143" .. -- U+23CF (⏏) "\226\143\169-\226\143\179" .. -- U+23E9-U+23F3 (⏩-⏳) "\226\143\184-\226\143\186" .. -- U+23F8-U+23FA (⏸-⏺) "\226\150\170" .. -- U+25AA (▪) "\226\150\171" .. -- U+25AB (▫) "\226\150\182" .. -- U+25B6 (▶) "\226\151\128" .. -- U+25C0 (◀) "\226\151\187-\226\151\190" .. -- U+25FB-U+25FE (◻-◾) "\226\152\128-\226\152\132" .. -- U+2600-U+2604 (☀-☄) "\226\152\142" .. -- U+260E (☎) "\226\152\145" .. -- U+2611 (☑) "\226\152\148" .. -- U+2614 (☔) "\226\152\149" .. -- U+2615 (☕) "\226\152\152" .. -- U+2618 (☘) "\226\152\157" .. -- U+261D (☝) "\226\152\160" .. -- U+2620 (☠) "\226\152\162" .. -- U+2622 (☢) "\226\152\163" .. -- U+2623 (☣) "\226\152\166" .. -- U+2626 (☦) "\226\152\170" .. -- U+262A (☪) "\226\152\174" .. -- U+262E (☮) "\226\152\175" .. -- U+262F (☯) "\226\152\184-\226\152\186" .. -- U+2638-U+263A (☸-☺) "\226\153\136-\226\153\147" .. -- U+2648-U+2653 (♈-♓) "\226\153\159" .. -- U+265F (♟) "\226\153\160" .. -- U+2660 (♠) "\226\153\163" .. -- U+2663 (♣) "\226\153\165" .. -- U+2665 (♥) "\226\153\166" .. -- U+2666 (♦) "\226\153\168" .. -- U+2668 (♨) "\226\153\187" .. -- U+267B (♻) "\226\153\190" .. -- U+267E (♾) "\226\153\191" .. -- U+267F (♿) "\226\154\146-\226\154\151" .. -- U+2692-U+2697 (⚒-⚗) "\226\154\153" .. -- U+2699 (⚙) "\226\154\155" .. -- U+269B (⚛) "\226\154\156" .. -- U+269C (⚜) "\226\154\160" .. -- U+26A0 (⚠) "\226\154\161" .. -- U+26A1 (⚡) "\226\154\170" .. -- U+26AA (⚪) "\226\154\171" .. -- U+26AB (⚫) "\226\154\176" .. -- U+26B0 (⚰) "\226\154\177" .. -- U+26B1 (⚱) "\226\154\189" .. -- U+26BD (⚽) "\226\154\190" .. -- U+26BE (⚾) "\226\155\132" .. -- U+26C4 (⛄) "\226\155\133" .. -- U+26C5 (⛅) "\226\155\136" .. -- U+26C8 (⛈) "\226\155\142" .. -- U+26CE (⛎) "\226\155\143" .. -- U+26CF (⛏) "\226\155\145" .. -- U+26D1 (⛑) "\226\155\147" .. -- U+26D3 (⛓) "\226\155\148" .. -- U+26D4 (⛔) "\226\155\169" .. -- U+26E9 (⛩) "\226\155\170" .. -- U+26EA (⛪) "\226\155\176-\226\155\181" .. -- U+26F0-U+26F5 (⛰-⛵) "\226\155\183-\226\155\186" .. -- U+26F7-U+26FA (⛷-⛺) "\226\155\189" .. -- U+26FD (⛽) "\226\156\130" .. -- U+2702 (✂) "\226\156\133" .. -- U+2705 (✅) "\226\156\136-\226\156\141" .. -- U+2708-U+270D (✈-✍) "\226\156\143" .. -- U+270F (✏) "\226\156\146" .. -- U+2712 (✒) "\226\156\148" .. -- U+2714 (✔) "\226\156\150" .. -- U+2716 (✖) "\226\156\157" .. -- U+271D (✝) "\226\156\161" .. -- U+2721 (✡) "\226\156\168" .. -- U+2728 (✨) "\226\156\179" .. -- U+2733 (✳) "\226\156\180" .. -- U+2734 (✴) "\226\157\132" .. -- U+2744 (❄) "\226\157\135" .. -- U+2747 (❇) "\226\157\140" .. -- U+274C (❌) "\226\157\142" .. -- U+274E (❎) "\226\157\147-\226\157\149" .. -- U+2753-U+2755 (❓-❕) "\226\157\151" .. -- U+2757 (❗) "\226\157\163" .. -- U+2763 (❣) "\226\157\164" .. -- U+2764 (❤) "\226\158\149-\226\158\151" .. -- U+2795-U+2797 (➕-➗) "\226\158\161" .. -- U+27A1 (➡) "\226\158\176" .. -- U+27B0 (➰) "\226\158\191" .. -- U+27BF (➿) "\226\164\180" .. -- U+2934 (⤴) "\226\164\181" .. -- U+2935 (⤵) "\226\172\133-\226\172\135" .. -- U+2B05-U+2B07 (⬅-⬇) "\226\172\155" .. -- U+2B1B (⬛) "\226\172\156" .. -- U+2B1C (⬜) "\226\173\144" .. -- U+2B50 (⭐) "\226\173\149" .. -- U+2B55 (⭕) "\227\128\176" .. -- U+3030 (〰) "\227\128\189" .. -- U+303D (〽) "\227\138\151" .. -- U+3297 (㊗) "\227\138\153" .. -- U+3299 (㊙) "\240\159\128\132" .. -- U+1F004 (🀄) "\240\159\131\143" .. -- U+1F0CF (🃏) "\240\159\133\176" .. -- U+1F170 (🅰) "\240\159\133\177" .. -- U+1F171 (🅱) "\240\159\133\190" .. -- U+1F17E (🅾) "\240\159\133\191" .. -- U+1F17F (🅿) "\240\159\134\142" .. -- U+1F18E (🆎) "\240\159\134\145-\240\159\134\154" .. -- U+1F191-U+1F19A (🆑-🆚) "\240\159\136\129" .. -- U+1F201 (🈁) "\240\159\136\130" .. -- U+1F202 (🈂) "\240\159\136\154" .. -- U+1F21A (🈚) "\240\159\136\175" .. -- U+1F22F (🈯) "\240\159\136\178-\240\159\136\186" .. -- U+1F232-U+1F23A (🈲-🈺) "\240\159\137\144" .. -- U+1F250 (🉐) "\240\159\137\145" .. -- U+1F251 (🉑) "\240\159\140\128-\240\159\153\143" .. -- U+1F300-U+1F64F (🌀-🙏) "\240\159\154\128-\240\159\155\151" .. -- U+1F680-U+1F6D7 (🚀-🛗) "\240\159\155\156-\240\159\155\172" .. -- U+1F6DC-U+1F6EC (🛜-🛬) "\240\159\155\176-\240\159\155\188" .. -- U+1F6F0-U+1F6FC (🛰-🛼) "\240\159\159\160-\240\159\159\171" .. -- U+1F7E0-U+1F7EB (🟠-🟫) "\240\159\159\176" .. -- U+1F7F0 (🟰) "\240\159\164\140-\240\159\169\147" .. -- U+1F90C-U+1FA53 (🤌-🩓) "\240\159\169\160-\240\159\169\173" .. -- U+1FA60-U+1FA6D (🩠-🩭) "\240\159\169\176-\240\159\169\188" .. -- U+1FA70-U+1FA7C (🩰-🩼) "\240\159\170\128-\240\159\170\137" .. -- U+1FA80-U+1FA89 (🪀-🪉) "\240\159\170\143-\240\159\171\134" .. -- U+1FA8F-U+1FAC6 (🪏-🫆) "\240\159\171\142-\240\159\171\156" .. -- U+1FACE-U+1FADC (🫎-🫜) "\240\159\171\159-\240\159\171\169" .. -- U+1FADF-U+1FAE9 (🫟-🫩) "\240\159\171\176-\240\159\171\184" -- U+1FAF0-U+1FAF8 (🫰-🫸) local unsupported_characters local function get_unsupported_characters() unsupported_characters, get_unsupported_characters = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_characters) do unsupported_characters[v] = k end return unsupported_characters end -- The list of unsupported titles and invert it (so the keys are pagenames and values are canonical titles). local unsupported_titles local function get_unsupported_titles() unsupported_titles, get_unsupported_titles = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_titles) do unsupported_titles[v] = k end return unsupported_titles end -- To save on memory, we only cache names with either non-ASCII characters in them or ASCII characters to be removed or -- transformed (apostrophe, double quote, hyphen). local L2_sort_key_cache = {} function export.get_L2_sort_key(L2) if L2 == "Translingual" then return "\1" elseif L2 == "English" then return "\2" elseif match(L2, "^[%z\1-\b\14-!#-&(-,.-\127]+$") then return L2 end local sort_key = L2_sort_key_cache[L2] if sort_key then return sort_key end sort_key = toNFC(ugsub(ugsub(toNFD(L2), "[" .. comb_chars_all .. "'\"ʻʼ]+", ""), "[%s%-]+", " ")) L2_sort_key_cache[L2] = sort_key return sort_key end --[==[ Given a pagename (or {nil} for the current page), create and return a data structure describing the page. The returned object includes the following fields: * `comb_chars`: A table containing various Lua character class patterns for different types of combined characters (those that decompose into multiple characters in the NFD decomposition). The patterns are meant to be used with {mw.ustring.find()}. The keys are: ** `single`: Single combining characters (character + diacritic), without surrounding brackets; ** `double`: Double combining characters (character + diacritic + character), without surrounding brackets; ** `vs`: Variation selectors, without surrounding brackets; ** `all`: Concatenation of `single` + `double` + `vs`, without surrounding brackets; ** `diacritics_single`: Like `single` but with surrounding brackets; ** `diacritics_double`: Like `double` but with surrounding brackets; ** `diacritics_all`: Like `all` but with surrounding brackets; ** `combined_single`: Lua pattern for matching a spacing character followed by one or more single combining characters; ** `combined_double`: Lua pattern for matching a combination of two spacing characters separated by one or more double combining characters, possibly also with single combining characters; * `emoji_pattern`: A Lua character class pattern (including surrounding brackets) that matches emojis. Meant to be used with {mw.ustring.find()}. * `L2_list`: Ordered list of L2 headings on the page, with the extra key `n` that gives the length of the list. * `L2_sections`: Lookup table of L2 headings on the page, where the key is the section number assigned by the preprocessor, and the value is the L2 heading name. Once an invocation has got its actual section number from get_current_L2 in [[Module:pages]], it can use this table to determine its parent L2. TODO: We could expand this to include subsections, to check POS headings are correct etc. * `unsupported_titles`: Map from pagenames to canonical titles for unsupported-title pages. * `namespace`: Namespace of the pagename. * `ns`: Namespace table for the page from mw.site.namespaces (TODO: merge with `namespace` above). * `full_raw_pagename`: Full version of the '''RAW''' pagename (i.e. unsupported-title pages aren't canonicalized); including the namespace and the base (portion before the slash). * `pagename`: Canonicalized subpage portion of the pagename (unsupported-title pages are canonicalized). * `pagename_with_base`: Same as `pagename` in the main namespace; otherwise, the whole pagename without the namespace. * `decompose_pagename`: Equivalent of `pagename` in NFD decomposition. * `pagename_len`: Length of `pagename` in Unicode chars, where combinations of spacing character + decomposed diacritic are treated as single characters. * `explode_pagename`: Set of characters found in `pagename`. The keys are characters (where combinations of spacing character + decomposed diacritic are treated as single characters). * `encoded_pagename`: FIXME: Document me. * `pagename_defaultsort`: FIXME: Document me. * `raw_defaultsort`: FIXME: Document me. * `wikitext_topic_cat`: FIXME: Document me. * `wikitext_langname_cat`: FIXME: Document me. `no_fetch_content` says to not fetch and parse the content or set a DEFAULTSORT sort key, in order to save time on test and documentation pages that have lots of template invocations that set `|pagename=`. It turns out nearly all the time of this function is contained in the line `frame:callParserFunction("DEFAULTSORT", data.pagename_defaultsort)`, so we skip it on test and documentation pages where it accomplishes nothing in any case. ]==] function export.process_page(pagename, no_fetch_content) local data = { comb_chars = comb_chars, emoji_pattern = "[" .. emoji_chars .. "]", unsupported_titles = unsupported_titles or get_unsupported_titles() } local cats = {} data.cats = cats -- We cannot store `raw_title` in `data` because it contains a metatable. local raw_title local function bad_pagename() if not pagename then error("Internal error: Something wrong, `data.pagename` not specified but current title contains illegal characters") else error(format("Bad value for `data.pagename`: '%s', which must not contain illegal characters", pagename)) end end if pagename then -- for testing, doc pages, etc. raw_title = new_title(pagename) if not raw_title then bad_pagename() end else raw_title = mw.title.getCurrentTitle() end local nsText = raw_title.nsText local namespace_is_reconstruction = nsText == "Reconstruction" data.namespace = nsText data.ns = mw.site.namespaces[raw_title.namespace] local full_raw_pagename = raw_title.fullText data.full_raw_pagename = full_raw_pagename local frame = mw.getCurrentFrame() -- WARNING: `content` may be nil, e.g. if we're substing a template like {{ja-new}} on a not-yet-created page -- or if the module specifies the subpage as `data.pagename` (which many modules do) and we're in an Appendix -- or other non-mainspace page. We used to make the latter an error but there are too many modules that do it, -- and substing on a nonexistent page is totally legit, and we don't actually need to be able to access the -- content of the page. local content = not no_fetch_content and raw_title:getContent() or nil -- Get the pagename. pagename = physical_to_logical_pagename_if_mammoth(raw_title) pagename = gsub(pagename, "^Unsupported titles/(.+)", function(m) insert(cats, "Unsupported titles") local title = (unsupported_titles or get_unsupported_titles())[m] if title then return title end -- Substitute pairs of "`". Those not used for escaping should be escaped as "`grave`", but might not be, -- so if a pair don't form a match, the closing "`" should become the opening "`" of the next match attempt. -- This has to be done manually, instead of using gsub. local open_pos = find(m, "`") if not open_pos then return m end title = {sub(m, 1, open_pos - 1)} while true do local close_pos = find(m, "`", open_pos + 1) if not close_pos then -- Add "`" plus any remaining characters. insert(title, sub(m, open_pos)) break end local escape = sub(m, open_pos, close_pos) local ch = (unsupported_characters or get_unsupported_characters())[escape] -- Match found, so substitute the character and move to the first "`" after the match if found, or -- otherwise return. if ch then insert(title, ch) local nxt_pos = close_pos + 1 open_pos = find(m, "`", nxt_pos) -- Add any characters between the match and the next "`" or end. if open_pos then insert(title, sub(m, nxt_pos, open_pos - 1)) else insert(title, sub(m, nxt_pos)) break end -- Match not found, so make the closing "`" the opening "`" of the next attempt. else -- Add the failed match, except for the closing "`". insert(title, sub(m, open_pos, close_pos - 1)) open_pos = close_pos end end return concat(title) end) -- Save pagename, as the local variable will be destructively modified. data.pagename = pagename if nsText == "" then data.pagename_with_base = pagename else data.pagename_with_base = raw_title.text end -- Decompose the pagename in Unicode normalization form D. data.decompose_pagename = toNFD(pagename) -- Explode the current page name into a character table, taking decomposed combining characters into account. local explode_pagename = {} local pagename_len = 0 local function explode(char) explode_pagename[char] = true pagename_len = pagename_len + 1 return "" end pagename = ugsub(pagename, comb_chars.combined_double, explode) pagename = gsub(ugsub(pagename, comb_chars.combined_single, explode), ".[\128-\191]*", explode) data.explode_pagename = explode_pagename data.pagename_len = pagename_len -- Generate DEFAULTSORT. data.encoded_pagename = encode_entities(data.pagename) data.pagename_defaultsort = get_lang("mul"):makeSortKey(data.encoded_pagename) if not no_fetch_content then frame:callParserFunction("DEFAULTSORT", data.pagename_defaultsort) end data.raw_defaultsort = uupper(raw_title.text) -- Make `L2_list` and `L2_sections`, note raw wikitext use of {{DEFAULTSORT:}} and {{DISPLAYTITLE:}}, then add categories if any unwanted L1 headings are found, the L2 headings are in the wrong order, or they don't match a canonical language name. -- Note: HTML comments shouldn't be removed from `content` until after this step, as they can affect the result. do local L2_list, L2_list_len, L2_sections = {}, 0, {} local prev, rc local new_cats, L2_wrong_order = {} local function handle_heading(heading) local level = heading.level if level > 2 then return end local name = heading:get_name() -- heading:get_name() will return nil if there are any newline characters in the preprocessed heading name (e.g. from an expanded template). In such cases, the preprocessor section count still increments (since it's calculated pre-expansion), but the heading will fail, so the L2 count shouldn't be incremented. if name == nil then return end L2_list_len = L2_list_len + 1 L2_list[L2_list_len] = name L2_sections[heading.section] = name -- Also add any L1s, since they terminate the preceding L2, but add a maintenance category since it's probably a mistake. if level == 1 then new_cats["Pages with unwanted L1 headings"] = true end -- Check the heading is in the right order. -- FIXME: we need a more sophisticated sorting method which handles non-diacritic special characters (e.g. Magɨ). if prev and not ( L2_wrong_order or string_compare(export.get_L2_sort_key(prev), export.get_L2_sort_key(name)) ) then new_cats["Pages with language headings in the wrong order"] = true L2_wrong_order = true end -- Check it's a canonical language name. if not (langnames or get_langnames())[name] then new_cats["Pages with nonstandard language headings"] = true end prev = name end local function handle_template(template) -- Turn off redirect checking except in the Reconstruction namespace because the rc flag is only -- used in the Reconstruction namespace and the other names are parser functions, which AFAIK can't -- be redirected to. local name = template:get_name(nil, not namespace_is_reconstruction and "no_redirect" or nil) if name == "DEFAULTSORT:" then new_cats["Pages with DEFAULTSORT conflicts"] = true elseif name == "DISPLAYTITLE:" then new_cats["Pages with DISPLAYTITLE conflicts"] = true elseif name == "reconstructed" then rc = true end end if content then for node in parse(content):iterate_nodes() do local node_class = class_else_type(node) if node_class == "heading" then handle_heading(node) elseif node_class == "template" then handle_template(node) elseif node_class == "parameter" then new_cats["Pages with raw triple-brace template parameters"] = true end end end L2_list.n = L2_list_len data.L2_list = L2_list data.L2_sections = L2_sections insert(cats, get_category("Pages with entries")) insert(cats, get_category(format("Pages with %s entr%s", L2_list_len, L2_list_len == 1 and "y" or "ies"))) for cat in pairs(new_cats) do insert(cats, get_category(cat)) end if namespace_is_reconstruction and not rc then local langname = match(full_raw_pagename, "^Reconstruction:([^/]+)/.") if langname then insert(cats, get_category(langname .. " entries missing Template:reconstructed")) end end end ------ 4. Parse page for maintenance categories. ------ -- Use of tab characters. if content and find(content, "\t", 1, true) then insert(cats, get_category("Pages with tab characters")) end -- Unencoded character(s) in title. local IDS = list_to_set{"⿰", "⿱", "⿲", "⿳", "⿴", "⿵", "⿶", "⿷", "⿸", "⿹", "⿺", "⿻", "⿼", "⿽", "⿾", "⿿", "㇯"} for char in pairs(explode_pagename) do if IDS[char] and char ~= data.pagename then insert(cats, "Terms containing unencoded characters") break end end -- Raw wikitext use of a topic or langname category. Also check if any raw sortkeys have been used. do local wikitext_topic_cat = {} local wikitext_langname_cat = {} local raw_sortkey -- If a raw sortkey has been found, add it to the relevant table. -- If there's no table (or the index is just `true`), create one first. local function add_cat_table(t, lang, sortkey) local t_lang = t[lang] if not sortkey then if not t_lang then t[lang] = true end return elseif t_lang == true or not t_lang then t_lang = {} t[lang] = t_lang end t_lang[uupper(decode_entities(sortkey))] = true end local function process_category(content, cat, colon, nxt) local pipe = find(cat, "|", colon + 1, true) -- Categories cannot end "|]]". if pipe == #cat then return end local title = new_title(pipe and sub(cat, 1, pipe - 1) or cat) if not (title and title.namespace == 14) then return end -- Get the sortkey (if any), then canonicalize category title. local sortkey = pipe and sub(cat, pipe + 1) or nil cat = title.text if sortkey then raw_sortkey = true -- If the sortkey contains "[", the first "]" of a final "]]]" is treated as part of the sortkey. if find(sortkey, "[", 1, true) and sub(content, nxt, nxt) == "]" then sortkey = sortkey .. "]" end end local code = match(cat, "^([%w%-.]+):") if code then add_cat_table(wikitext_topic_cat, code, sortkey) return end -- Split by word. cat = split(cat, " ", true, true) -- Formerly we looked for the language name anywhere in the category. This is simply wrong -- because there are no categories like 'Alsatian French lemmas' (only L2 languages -- have langname categories), but doing it this way wrongly catches things like [[Category:Shapsug Adyghe]] -- in [[Category:Adyghe entries with language name categories using raw markup]]. local n = #cat - 1 if n <= 0 then return end -- Go from longest to shortest and stop once we've found a language name. Going from shortest -- to longest or not stopping after a match risks falsely matching (e.g.) German Low German -- categories as German. repeat local name = concat(cat, " ", 1, n) if (langnames or get_langnames())[name] then add_cat_table(wikitext_langname_cat, name, sortkey) return end n = n - 1 until n == 0 end if content then -- Remove comments, then iterate over category links. content = remove_comments(content, "BOTH") local head = find(content, "[[", 1, true) while head do local close = find(content, "]]", head + 2, true) if not close then break end -- Make sure there are no intervening "[[" between head and close. local open = find(content, "[[", head + 2, true) while open and open < close do head = open open = find(content, "[[", head + 2, true) end local cat = sub(content, head + 2, close - 1) -- Locate the colon, and weed out most unwanted links. "[ _\128-\244]*" catches valid whitespace, and ensures any category links using the colon trick are ignored. We match all non-ASCII characters, as there could be multibyte spaces, and mw.title.new will filter out any remaining false-positives; this is a lot faster than running mw.title.new on every link. local colon = match(cat, "^[ _\128-\244]*[Cc][Aa][Tt][EeGgOoRrYy _\128-\244]*():") if colon then process_category(content, cat, colon, close + 2) end head = open end end data.wikitext_topic_cat = wikitext_topic_cat data.wikitext_langname_cat = wikitext_langname_cat if raw_sortkey then insert(cats, get_category("Pages with raw sortkeys")) end end return data end return export 0esb0zcz2sjpy8aqh1uv1deoe3q625h Module:pages 828 7979 54718 2026-09-25T18:04:31Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local string_utilities_module = "Module:string utilities" local concat = table.concat local find = string.find local format = string.format local getmetatable = getmetatable local get_current_section -- defined below local get_namespace_shortcut -- defined below local get_pagetype -- defined below local gsub = string.gsub local insert = table.insert local is_internal_title -- defined below local is_title -- defined below local lower = string.l...' 54718 Scribunto text/plain local export = {} local string_utilities_module = "Module:string utilities" local concat = table.concat local find = string.find local format = string.format local getmetatable = getmetatable local get_current_section -- defined below local get_namespace_shortcut -- defined below local get_pagetype -- defined below local gsub = string.gsub local insert = table.insert local is_internal_title -- defined below local is_title -- defined below local lower = string.lower local match = string.match local new_title = mw.title.new local require = require local sub = string.sub local title_equals = mw.title.equals local tonumber = tonumber local type = type local ufind = mw.ustring.find local unstrip_nowiki = mw.text.unstripNoWiki --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local current_frame local function get_current_frame() current_frame, get_current_frame = mw.getCurrentFrame(), nil return current_frame end local parent_frame local function get_parent_frame() parent_frame, get_parent_frame = (current_frame or get_current_frame()):getParent(), nil return parent_frame end local namespace_shortcuts local function get_namespace_shortcuts() namespace_shortcuts, get_namespace_shortcuts = { [4] = "WT", [10] = "T", [14] = "CAT", [100] = "AP", [110] = "WS", [118] = "RC", [828] = "MOD", }, nil return namespace_shortcuts end do local transcluded --[==[ Returns {true} if the current {{tl|#invoke:}} is being transcluded, or {false} if not. If the current {{tl|#invoke:}} is part of a template, for instance, this template will therefore return {true}. Note that if a template containing an {{tl|#invoke:}} is used on its own page (e.g. to display a demonstration), this function is still able to detect that this is transclusion. This is an improvement over the other method for detecting transclusion, which is to check the parent frame title against the current page title, which fails to detect transclusion in that instance.]==] function export.is_transcluded() if transcluded == nil then transcluded = (parent_frame or get_parent_frame()) and parent_frame:preprocess("<includeonly>1</includeonly>") == "1" or false end return transcluded end end do local preview --[==[ Returns {true} if the page is currently being viewed in preview, or {false} if not.]==] function export.is_preview() if preview == nil then preview = (current_frame or get_current_frame()):preprocess("{{REVISIONID}}") == "" end return preview end end --[==[ Returns {true} if the input is a title object, or {false} if not. This therefore '''includes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_internal_title` below.]==] function export.is_title(val) if not (val and type(val) == "table") then return false end local mt = getmetatable(val) -- There's no foolproof method for checking for a title object, but the -- __eq metamethod should be mw.title.equals unless the object has been -- seriously messed around with. return mt and type(mt) == "table" and getmetatable(mt) == nil and mt.__eq == title_equals and true or false end is_title = export.is_title --[==[ Returns {true} if the input is an internal title object, or {false} if not. An internal title object is a title object for a page on this wiki, such as [[example]]. This therefore '''excludes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_title` above.]==] function export.is_internal_title(title) -- Note: Mainspace titles starting with "#" should be invalid, but a bug in mw.title.new and mw.title.makeTitle means a title object is returned that has the empty string for prefixedText, so they need to be filtered out. return is_title(title) and #title.prefixedText > 0 and #title.interwiki == 0 end is_internal_title = export.is_internal_title --[==[ Returns {true} if the input string is a valid link target, or {false} if not. This therefore '''includes''' link targets to other wikis, such as [[w:Example]], unlike `is_valid_page_name` below.]==] function export.is_valid_link_target(target) local target_type = type(target) if target_type == "string" then return is_title(new_title(target)) end error(format("bad argument #1 to 'is_valid_link_target' (string expected, got %s)", target_type), 2) end --[==[ Returns {true} if the input string is a valid page name on this wiki, or {false} if not. This therefore '''excludes''' page names on other wikis, such as [[w:Example]], unlike `is_valid_link_target` above.]==] function export.is_valid_page_name(name) local name_type = type(name) if name_type == "string" then return is_internal_title(new_title(name)) end error(format("bad argument #1 to 'is_valid_page_name' (string expected, got %s)", name_type), 2) end --[==[ Given a title object, returns a full link target which will always unambiguously link to it. For instance, the input {"foo"} (for the page [[foo]]) returns {":foo"}, as a leading colon always refers to mainspace, even when other namespaces might be assumed (e.g. when transcluding using `{{ }}` syntax). If `shortcut` is set, then the returned target will use the namespace shortcut, if any; for example, the title for `Template:foo` would return {"T:foo"} instead of {"Template:foo"}.]==] function export.get_link_target(title, shortcut) if not is_title(title) then error(format("bad argument #1 to 'is_valid_link_target' (title object expected, got %s)", type(title))) elseif title.interwiki ~= "" then return title.fullText elseif shortcut then local fragment = title.fragment if fragment == "" then return get_namespace_shortcut(title) .. ":" .. title.text end return get_namespace_shortcut(title) .. ":" .. title.text .. "#" .. fragment elseif title.namespace == 0 then return ":" .. title.fullText end return title.fullText end do local function find_sandbox(text) return find(text, "^User:.") or find(lower(text), "sandbox", 1, true) end local function get_transclusion_subtypes(title, main_type, documentation, page_suffix) local text, subtypes = title.text, {main_type} -- Any template/module with "sandbox" in the title. These are impossible -- to screen for more accurately, as there's no consistent pattern. Also -- any user sandboxes in the form (e.g.) "Template:User:...". local sandbox = find_sandbox(text) if sandbox then insert(subtypes, "sandbox") end -- Any template/module testcases (which can be labelled and/or followed -- by further subpages). local testcase = find(text, "./[Tt]estcases?%f[%L]") if testcase then -- Order "testcase" and "sandbox" based on where the patterns occur -- in the title. local n = sandbox and sandbox < testcase and 3 or 2 insert(subtypes, n, "testcase") end -- Any template/module documentation pages. if documentation then insert(subtypes, "documentation") end local final = subtypes[#subtypes] if not (final == main_type and not page_suffix or final == "sandbox") then insert(subtypes, "page") end return concat(subtypes, " ") end local function get_snippet_subtypes(title, main_type, documentation) local ns = title.namespace return get_transclusion_subtypes(title, ( ns == 2 and "user " or ns == 8 and match(title.text, "^Gadget-.") and "gadget " or "" ) .. main_type, documentation) end --[==[ Returns the page type of the input title object in a format which can be used in running text.]==] function export.get_pagetype(title) if not is_internal_title(title) then error(mw.dumpObject(title.fullText) .. " is not a valid page name.") end -- If possibly a documentation page, get the base title and set the -- `documentation` flag. local content_model, text, documentation = title.contentModel if content_model == "wikitext" then text = title.text if title.isSubpage and title.subpageText == "documentation" then local base_title = title.basePageTitle if base_title then title, content_model, text, documentation = base_title, base_title.contentModel, base_title.text, true end end end -- Content models have overriding priority, as they can appear in -- nonstandard places due to page content model changes. if content_model == "css" or content_model == "sanitized-css" then return get_snippet_subtypes(title, "stylesheet", documentation) elseif content_model == "javascript" then return get_snippet_subtypes(title, "script", documentation) elseif content_model == "json" then return get_snippet_subtypes(title, "JSON data", documentation) elseif content_model == "MassMessageListContent" then return get_snippet_subtypes(title, "mass message delivery list", documentation) -- Modules. elseif content_model == "Scribunto" then return get_transclusion_subtypes(title, "module", documentation, false) elseif content_model == "text" then return "page" -- ??? -- Otherwise, the content model is "wikitext", so check namespaces. elseif title.isTalkPage then return "talk page" end local ns = title.namespace -- Main namespace. if ns == 0 then return "entry" -- Wiktionary: elseif ns == 4 then return find_sandbox(title.text) and "sandbox" or "project page" -- Template: elseif ns == 10 then return get_transclusion_subtypes(title, "template", documentation, false) end -- Convert the namespace to lowercase, unless it contains a capital -- letter after the initial letter (e.g. MediaWiki, TimedText). Also -- normalize any underscores. local ns_text = gsub(title.nsText, "_", " ") if ufind(ns_text, "^%U*$", 2) then ns_text = ulower(ns_text) end -- User: if ns == 2 then return ns_text .. " " .. (title.isSubpage and "subpage" or "page") -- Category: and Appendix: elseif ns == 14 or ns == 100 then return ns_text -- Thesaurus: and Reconstruction: elseif ns == 110 or ns == 118 then return ns_text .. " entry" end return ns_text .. " page" end get_pagetype = export.get_pagetype end --[==[ Returns {true} if the input title object is for a content page, or {false} if not. A content page is a page that is considered part of the dictionary itself, and excludes pages for discussion, administration, maintenance etc.]==] function export.is_content_page(title) if not is_internal_title(title) then error(mw.dumpObject(title.fullText) .. " is not a valid page name.") end local ns = title.namespace -- (main), Appendix, Thesaurus, Citations, Reconstruction. return (ns == 0 or ns == 100 or ns == 110 or ns == 114 or ns == 118) and title.contentModel == "wikitext" end --[==[ Returns {true} if the input title object is for a documentation page, or {false} if not.]==] function export.is_documentation(title) return match(get_pagetype(title), "%f[%w]documentation%f[%W]") and true or false end --[==[ Returns {true} if the input title object is for a sandbox, or {false} if not. By default, sandbox documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==] function export.is_sandbox(title, include_documentation) local pagetype = get_pagetype(title) return match(pagetype, "%f[%w]sandbox%f[%W]") and ( include_documentation or not match(pagetype, "%f[%w]documentation%f[%W]") ) and true or false end --[==[ Returns {true} if the input title object is for a testcase page, or {false} if not. By default, testcase documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==] function export.is_testcase_page(title, include_documentation) local pagetype = get_pagetype(title) return match(pagetype, "%f[%w]testcase%f[%W]") and ( include_documentation or not match(pagetype, "%f[%w]documentation%f[%W]") ) and true or false end --[==[ Returns the namespace shortcut for the input title object, or else the namespace text. For example, a `Template:` title returns {"T"}, a `Module:` title returns {"MOD"}, and a `User:` title returns {"User"}.]==] function export.get_namespace_shortcut(title) return (namespace_shortcuts or get_namespace_shortcuts())[title.namespace] or title.nsText end get_namespace_shortcut = export.get_namespace_shortcut do local function check_level(lvl) if type(lvl) ~= "number" then error("Heading levels must be numbers.") elseif lvl < 1 or lvl > 6 or lvl % 1 ~= 0 then error("Heading levels must be integers between 1 and 6.") end return lvl end --[==[ A helper function which iterates over the headings in `text`, which should be the content of a page or (main) section. Each iteration returns three values: `sec` (the section title), `lvl` (the section level) and `loc` (the index of the section in the given text, from the first equals sign). The section title will be automatically trimmed, and any HTML entities will be resolved. The optional parameter `a` (which should be an integer between 1 and 6) can be used to ensure that only headings of the specified level are iterated over. If `b` is also given, then they are treated as a range. The optional parameters `a` and `b` can be used to specify a range, so that only headings with levels in that range are returned.]==] local function find_headings(text, a, b) a = a and check_level(a) or nil b = b and check_level(b) or a or nil local start, loc, lvl, sec = 1 return function() repeat loc, lvl, sec, start = match(text, "()%f[^%z\n](==?=?=?=?=?)([^\n]+)%2[\t ]*%f[%z\n]()", start) lvl = lvl and #lvl until not (sec and a) or (lvl >= a and lvl <= b) return sec and trim(decode_entities(sec)) or nil, lvl, loc end end local function _get_section(content, name, level) if not (content and name) then return nil elseif find(name, "\n", 1, true) then error("Heading name cannot contain a newline.") end level = level and check_level(level) or nil name = trim(decode_entities(name)) local start for sec, lvl, loc in find_headings(content, level and 1 or nil, level) do if start and lvl <= level then return sub(content, start, loc - 1) elseif not start and (not level or lvl == level) and sec == name then start, level = loc, lvl end end return start and sub(content, start) end --[==[ A helper function to return the content of a page section. `content` is raw wikitext, `name` is the requested section, and `level` is an optional parameter that specifies the required section heading level. If `level` is not supplied, then the first section called `name` is returned. `name` can either be a string or table of section names. If a table, each name represents a section that has the next as a subsection. For example, { {"Spanish", "Noun"}} will return the first matching section called "Noun" under a section called "Spanish". These do not have to be at adjacent levels ("Noun" might be L4, while "Spanish" is L2). If `level` is given, it refers to the last name in the table (i.e. the name of the section to be returned). The returned section includes all of its subsections. If no matching section is found, return {nil}.]==] function export.get_section(content, names, level) if type(names) ~= "table" then return _get_section(content, names, level) end local i = 1 local name = names[i] if not name then error("Must specify at least 1 section.") end while true do local nxt_i = i + 1 local nxt = names[nxt_i] if nxt == nil then return _get_section(content, name, level) end content = _get_section(content, name) if content == nil then return nil elseif i == 6 then error("Not possible specify more than 6 sections: headings only go up to level 6.") end i = nxt_i name = names[i] end return content end end --[==[ Convert a physical pagename (or the current pagename, if {nil} is passed in) to its logical equivalent, but only if the physical pagename refers to one of the splits of a mammoth page such as [[a]]. Otherwise this simply returns the subpage (the part after the last slash in namespace other than the main one, otherwise the same as passed in), unless `include_base` is specified, in which case the return value will be the whole pagename minus the namespace, including the base (the part before the final slash). FIXME: This should be augmented with logic to handle unsupported titles, which is currently in process_page() in [[Module:headword/page]], so that it is a general physical-to-logical conversion function. Examples: * {physical_to_logical_pagename_if_mammoth("a")} → {"a"} * {physical_to_logical_pagename_if_mammoth("a/languages A to L")} → {"a"} (since [[a]] is marked as a mammoth page in [[Module:links/data]]) * {physical_to_logical_pagename_if_mammoth("50/50")} → {"50/50"} (since [[50/50]] is in the main namespace) * {physical_to_logical_pagename_if_mammoth("Appendix:Lojban/a")} → "a" * {physical_to_logical_pagename_if_mammoth("Appendix:Lojban/a", true)} → "Lojban/a" * {physical_to_logical_pagename_if_mammoth("Reconstruction:Proto-Slavic/a")} → "a" * {physical_to_logical_pagename_if_mammoth("Reconstruction:Proto-Slavic/a", true)} → "Proto-Slavic/a" * {physical_to_logical_pagename_if_mammoth("User:Example/sandbox/foo")} → "foo" * {physical_to_logical_pagename_if_mammoth("User:Example/sandbox/foo", true)} → "Example/sandbox/foo" ]==] function export.physical_to_logical_pagename_if_mammoth(title, include_base) if title == nil then title = mw.title.getCurrentTitle() elseif not is_title(title) then title = mw.title.new(title) end if title.nsText == "" then -- Formerly we checked for the specific known subpages of a given mammoth split page, e.g. we would convert -- [[a/languages M to Z]] to [[a]] assuming that [[a/languages M to Z]] was one of the splits, but not -- [[a/languages N to Z]]. To simplify this, we just convert anything with the right mammoth split page format -- on the assumption that it's unlikely we will ever have a legitimate non-mammoth-split pagename of this sort. local pagename = title.text local mammoth_root_page = pagename:match("^(.*)/languages [A-Z] to [A-Z]$") if mammoth_root_page then pagename = mammoth_root_page end return pagename elseif include_base then return title.text else return title.subpageText end end --[==[ Obsolete name for `physical_to_logical_pagename_if_mammoth`. FIXME: Replace all uses and remove. ]==] function export.safe_page_name(title) return export.physical_to_logical_pagename_if_mammoth(title) end do local current_section --[==[ A function which returns the number of the page section which contains the current {#invoke}, or {0} if the section can't be determined.]==] function export.get_current_section() if current_section ~= nil then return current_section end local frame = parent_frame or get_parent_frame() while frame do if find(frame:getTitle(), "^Module:") then current_section = 0 return 0 end frame = frame:getParent() end local extension_tag = (current_frame or get_current_frame()).extensionTag -- We determine the section via the heading strip marker count, since they're numbered sequentially, but the only way to do this is to generate a fake heading via frame:preprocess(). The native parser assigns each heading a unique marker, but frame:preprocess() will return copies of older markers if the heading is identical to one further up the page, so the fake heading has to be unique to the page. The best way to do this is to feed it a heading containing a nowiki marker (which we will need later), since those are always unique. local nowiki_marker = extension_tag(current_frame, "nowiki") -- Note: heading strip markers have a different syntax to the ones used for tags. local h = tonumber(match( current_frame:preprocess("=" .. nowiki_marker .. "="), "\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127" )) -- For some reason, [[Special:ExpandTemplates]] doesn't generate a heading strip marker, so if that happens we simply abort early. if not h then return 0 end -- The only way to get the section number is to increment the heading count, so we store the offset in nowiki strip markers which can be retrieved by procedurally unstripping nowiki markers, counting backwards until we find a match. local n, offset = tonumber(match( nowiki_marker, "\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127" ), 16) while not offset and n > 0 do n = n - 1 offset = match( unstrip_nowiki(format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)), "^HEADING\1(%d+)" -- Prefix "HEADING\1" prevents collisions. ) end offset = offset and (offset + 1) or 0 extension_tag(current_frame, "nowiki", "HEADING\1" .. offset) current_section = h - offset return current_section end get_current_section = export.get_current_section end do local L2_sections, current_L2 local function get_L2_sections() L2_sections, get_L2_sections = mw.loadData("Module:headword/data").page.L2_sections, nil return L2_sections end --[==[ A function which returns the name of the L2 language section which contains the current {#invoke}.]==] function export.get_current_L2() if current_L2 ~= nil then return current_L2 or nil -- Return nil if current_L2 is false (i.e. there's no L2). end local section = get_current_section() while section > 0 do local L2 = (L2_sections or get_L2_sections())[section] if L2 then current_L2 = L2 return L2 end section = section - 1 end current_L2 = false return nil end end return export nhxd4vmvm0np9znoomlx3hbv4pf399c Module:languages/data/3/m 828 7980 54719 2026-09-25T18:05:54Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["maa"] = { "San Jerónimo Tecóatl Mazatec", 7692927, "omq-maz", "Latn", } m["mab"] = { "Yutanduchi Mixtec", 12645448, "omq-mxt", "Latn", } m["ma...' 54719 Scribunto text/plain local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["maa"] = { "San Jerónimo Tecóatl Mazatec", 7692927, "omq-maz", "Latn", } m["mab"] = { "Yutanduchi Mixtec", 12645448, "omq-mxt", "Latn", } m["mad"] = { "Madurese", 36213, "poz-msa", "Latn, Java", } m["mae"] = { "Bo-Rukul", 34967, "nic-ple", "Latn", } m["maf"] = { "Mafa", 35819, "cdc-cbm", "Latn", } m["mag"] = { "Magahi", -- Not to be confused with Magadhi Prakrit (pra-mag) 33728, "inc-bih", "Deva, Kthi", translit = { Deva = "bho-translit", Kthi = "bho-Kthi-translit", }, } m["mai"] = { "Maithili", 36109, "inc-bih", "Deva, Tirh, Kthi, Newa", translit = { Deva = "mai-translit", Tirh = "mai-Tirh-translit", Kthi = "bho-Kthi-translit", }, } m["maj"] = { "Jalapa de Díaz Mazatec", 3915999, "omq-maz", "Latn", } m["mak"] = { "Makasar", 33643, "poz-ssw", "Latn, Bugi, Maka", } m["mam"] = { "Mam", 33467, "myn", "Latn", } m["man"] = { "Mandingo", 35772, "dmn-man", "Latn", } m["maq"] = { "Chiquihuitlán Mazatec", 5101757, "omq-maz", "Latn", } m["mas"] = { "Maasai", 35787, "sdv-lma", "Latn", } m["mat"] = { "Matlatzinca", 12953704, "omq", "Latn", } m["mau"] = { "Huautla Mazatec", 36230, "omq-maz", "Latn", } m["mav"] = { "Sateré-Mawé", 6794475, "tup", "Latn", } m["maw"] = { "Mampruli", 35804, "nic-wov", "Latn", } m["max"] = { "North Moluccan Malay", 7056136, "crp", "Latn", ancestors = "ms", } m["maz"] = { "Central Mazahua", 36228, "oto", "Latn", } m["mba"] = { "Higaonon", 5753411, "mno", "Latn", } m["mbb"] = { "Western Bukidnon Manobo", 7987643, "mno", "Latn", } m["mbc"] = { "Macushi", 56633, "sai-pem", "Latn", } m["mbd"] = { "Dibabawon Manobo", 18755523, "mno", "Latn", } m["mbe"] = { "Molale", 3319444, "nai-plp", "Latn", } m["mbf"] = { "Baba Malay", 18642798, "crp", "Latn", ancestors = "ms", } m["mbh"] = { "Mangseng", 6749147, "poz-ocw", "Latn", } m["mbi"] = { "Ilianen Manobo", 14916911, "mno", "Latn", } m["mbj"] = { "Nadëb", 3335011, "sai-nad", "Latn", } m["mbk"] = { "Malol", 6744477, "poz-ocw", "Latn", } m["mbl"] = { "Maxakalí", 3029682, "sai-mje", "Latn", } m["mbm"] = { "Ombamba", 36407, "bnt-mbt", "Latn", } m["mbn"] = { "Macaguán", 3273980, "sai-guh", "Latn", } m["mbo"] = { -- is, like 'bqz', 'bsi' and 'bss', a dialect of Manenguba "Mbo (Cameroon)", 36011, "bnt-mne", "Latn", } m["mbp"] = { "Wiwa", 3012604, "cba", "Latn", } m["mbq"] = { "Maisin", 3448149, nil, "Latn", } m["mbr"] = { "Nukak Makú", 3346228, "sai-nad", "Latn", } m["mbs"] = { "Sarangani Manobo", 7423093, "mno", "Latn", } m["mbt"] = { "Matigsalug Manobo", 6787447, "mno", "Latn", } m["mbu"] = { "Mbula-Bwazza", 3913324, "nic-jrn", "Latn", } m["mbv"] = { "Mbulungish", 36003, "alv-nal", "Latn", } m["mbw"] = { "Maring", 3293280, "ngf-jim", "Latn", } m["mbx"] = { "Mari (Sepik)", 6760942, "paa-sep", "Latn", } m["mby"] = { "Memoni", 4180871, "inc-snd", "Gujr, Aran", } m["mbz"] = { "Amoltepec Mixtec", 13583504, "omq-mxt", "Latn", } m["mca"] = { "Maca", 3281043, "sai-mtc", "Latn", } m["mcb"] = { "Machiguenga", 3915441, "awd", "Latn", } m["mcc"] = { "Bitur", 4919173, "paa-tir", "Latn", } m["mcd"] = { "Sharanahua", 12953881, "sai-pan", "Latn", } m["mce"] = { "Itundujia Mixtec", 12953727, "omq-mxt", "Latn", } m["mcf"] = { "Matsés", 2981620, "sai-pan", "Latn", } m["mcg"] = { "Mapoyo", 56946, "sai-map", "Latn", } m["mch"] = { "Ye'kwana", 3082027, "sai-car", "Latn", sort_key = { remove_diacritics = "%-%s", from = {"'", "ñ", "ö", "sh", "ü"}, to = {"’", "n" .. p[1], "o" .. p[1], "s" .. p[1], "u" .. p[1]} } } m["mci"] = { "Mese", 6821190, "ngf-san", "Latn", } m["mcj"] = { "Mvanip", 3913281, "nic-mmb", "Latn", } m["mck"] = { "Mbunda", 34170, "bnt-clu", "Latn", } m["mcl"] = { "Macaguaje", 6722435, "sai-tuc", "Latn", } m["mcm"] = { "Kristang", 2669169, "crp", "Latn", ancestors = "pt", } m["mcn"] = { "Masana", 56668, "cdc-mas", } m["mco"] = { "Coatlán Mixe", 25559716, "nai-miz", "Latn", } m["mcp"] = { "Makaa", 35803, "bnt-mka", "Latn", } m["mcq"] = { "Ese", 5397551, "ngf-koi", "Latn", } m["mcr"] = { "Menya", 11732444, "ngf-kme", "Latn", } m["mcs"] = { "Mambai", 6748872, "alv-mbm", "Latn", } m["mcu"] = { "Cameroon Mambila", 19359039, "nic-mmb", "Latn", } -- mcv (Minanibai) merged into ffi (Foia Foia) per Glottolog m["mcw"] = { "Mawa", 3441333, "cdc-est", "Latn", } m["mcx"] = { "Mpiemo", 35908, "bnt-bek", "Latn", } m["mcy"] = { "South Watut", 12953293, "poz-ocw", "Latn", } m["mcz"] = { "Mawan", 11732429, "ngf-han", "Latn", } m["mda"] = { "Mada (Nigeria)", 3915843, "nic-nin", "Latn", } m["mdb"] = { "Morigi", 6912195, "paa-kiw", "Latn", } m["mdc"] = { "Male", 6742927, "ngf-min", "Latn", } m["mdd"] = { "Mbum", 36170, "alv-mbm", "Latn", } m["mde"] = { "Bura Mabang", 35860, "ssa", "Arab, Latn", } m["mdf"] = { "Moksha", 13343, "urj-mdv", "Cyrl", translit = "mdf-translit", strip_diacritics = {remove_diacritics = c.acute}, override_translit = true, sort_key = "mdf-sortkey", } m["mdg"] = { "Massalat", 759984, nil, "Latn", } m["mdh"] = { "Maguindanao", 33717, "phi", "Latn, Arab", } m["mdi"] = { "Mamvu", 3033594, "csu-mle", "Latn", } m["mdj"] = { "Mangbetu", 56327, "csu-maa", "Latn", } m["mdk"] = { "Mangbutu", 6748877, "csu-mle", "Latn", } m["mdl"] = { "Maltese Sign Language", 6744816, "sgn", } m["mdm"] = { "Mayogo", 6797580, "nic-nke", "Latn", } m["mdn"] = { "Mbati", 36165, "bnt-ngn", "Latn", } m["mdp"] = { "Mbala", 6799583, "bnt-pen", "Latn", } m["mdq"] = { "Mbole", 6799727, "bnt-mbe", "Latn", } m["mdr"] = { "Mandar", 35995, "poz-ssw", "Bugi, Latn", } m["mds"] = { "Maria", 3448673, "paa-man", "Latn", } m["mdt"] = { "Mbere", 36062, "bnt-mbt", "Latn", } m["mdu"] = { "Mboko", 36058, "bnt-mbo", "Latn", } m["mdv"] = { "Santa Lucía Monteverde Mixtec", 12953722, "omq-mxt", "Latn", } m["mdw"] = { "Mbosi", 36035, "bnt-mbo", "Latn", } m["mdx"] = { "Dizin", 35313, "omv-diz", "Ethi, Latn", } m["mdy"] = { "Maale", 795327, "omv-ome", "Latn", } m["mdz"] = { "Suruí Do Pará", 10322149, "tup-gua", "Latn", } m["mea"] = { "Menka", 36078, "nic-grs", "Latn", } m["meb"] = { "Ikobi-Mena", 11732241, "paa-tki", "Latn", } m["mec"] = { "Mara", 6772774, } m["med"] = { "Melpa", 36166, "ngf-hag", "Latn", } m["mee"] = { "Mengen", 3305831, "poz-ocw", "Latn", } m["mef"] = { "Megam", 6808589, } m["meh"] = { "Southwestern Tlaxiaco Mixtec", 7070686, "omq-mxt", "Latn", } m["mei"] = { "Midob", 36007, "nub", "Latn", } m["mej"] = { "Meyah", 11732436, "paa-ebh", "Latn", } m["mek"] = { "Mekeo", 3304803, "poz-ocw", "Latn", } m["mel"] = { "Central Melanau", 18638319, "poz-swa", "Latn", } m["mem"] = { "Mangala", 6748664, } m["men"] = { "Mende (Sierra Leone)", 1478672, "dmn-msw", "Latn, Mend", } m["meo"] = { "Kedah Malay", 4925684, "poz-mly", "Latn, Arab, Thai", strip_diacritics = { from = {u(0xF70F)}, to = {"ญ"} }, sort_key = {Thai = "Thai-sortkey"}, } m["mep"] = { "Miriwoong", 3111847, "aus-jar", "Latn", } m["meq"] = { "Merey", 3502314, "cdc-cbm", "Latn", } m["mer"] = { "Meru", 13313, "bnt-kka", "Latn", } m["mes"] = { "Masmaje", 3440448, } m["met"] = { "Mato", 3299190, "poz-ocw", "Latn", } m["meu"] = { "Motu", 33516, "poz-ocw", "Latn", } m["mev"] = { "Mano", 3913286, "dmn-mda", "Latn", } m["mew"] = { "Maaka", 3438764, "cdc-wst", "Latn", } m["mey"] = { "Hassaniya Arabic", 56231, "sem-arb", "Arab", } m["mez"] = { "Menominee", 13363, "alg", "Latn", sort_key = {remove_diacritics = "·"}, } m["mfa"] = { "Pattani Malay", 1199751, "poz-mly", "Latn, Arab, Thai", strip_diacritics = { from = {u(0xF70F)}, to = {"ญ"} }, sort_key = {Thai = "Thai-sortkey"}, } m["mfb"] = { "Bangka", 3258818, "poz-mly", "Latn, Arab", } m["mfc"] = { "Mba", 4286464, "nic-mbc", "Latn", } m["mfd"] = { "Mendankwe-Nkwen", 11129537, "nic-nge", "Latn", } m["mfe"] = { "Mauritian Creole", 33661, "crp", "Latn", ancestors = "fr", sort_key = s["roa-oil-sortkey"], } m["mff"] = { "Naki", 36083, "nic-bbe", "Latn", } m["mfg"] = { "Mixifore", 3914478, "dmn-mok", } m["mfh"] = { "Matal", 3501751, "cdc-cbm", "Latn", } m["mfi"] = { "Wandala", 3441249, "cdc-cbm", "Latn", } m["mfj"] = { "Mefele", 3501871, "cdc-cbm", "Latn", } m["mfk"] = { "North Mofu", 56303, "cdc-cbm", "Latn", } m["mfl"] = { "Putai", 56291, "cdc-cbm", "Latn", } m["mfm"] = { "Marghi South", 56248, } m["mfn"] = { "Cross River Mbembe", 3915395, "nic-uce", "Latn", } m["mfo"] = { "Mbe", 36075, "nic-eko", "Latn", } m["mfp"] = { "Makassar Malay", 12952776, "qfa-mix", "Latn", ancestors = "ms, mak" } m["mfq"] = { "Moba", 19921578, "nic-grm", "Latn", } m["mfr"] = { "Marrithiyel", 6773014, "aus-dal", "Latn", } m["mfs"] = { "Mexican Sign Language", 3915511, "sgn", "Latn", -- when documented } m["mft"] = { "Mokerang", 3319387, "poz-aay", "Latn", } m["mfu"] = { "Mbwela", 11004988, "bnt-clu", ancestors = "lch", } m["mfv"] = { "Mandjak", 35822, "alv-pap", } m["mfw"] = { "Mulaha", 6933720, "paa-kwa", "Latn", } m["mfx"] = { "Melo", 6813268, "omv-nom", } m["mfy"] = { "Mayo", 56729, "azc-trc", "Latn", sort_key = {remove_diacritics = c.acute}, } m["mfz"] = { "Mabaan", 20526385, "sdv", "Latn", } m["mga"] = { "Middle Irish", 36116, "cel-gae", "Latn", ancestors = "sga", strip_diacritics = {remove_diacritics = c.dotabove .. c.diaer .. "·"}, sort_key = "mga-sortkey", } m["mgb"] = { "Mararit", 56359, "sdv-tmn", "Latn", } m["mgc"] = { "Morokodo", 6913216, "csu-bbk", "Latn", } m["mgd"] = { "Moru", 6915014, "csu-mma", "Latn, Arab", } m["mge"] = { "Mango", 713659, "csu-sar", "Latn", } m["mgf"] = { "Maklew", 6739816, "paa-bul", "Latn", } m["mgg"] = { "Mpongmpong", 35924, "bnt-bek", } m["mgh"] = { "Makhuwa-Meetto", 33604, "bnt-mak", "Latn", ancestors = "vmw", } m["mgi"] = { "Jili", 3914497, "nic-pls", } m["mgj"] = { "Abureni", 3441256, "nic-cde", "Latn", } m["mgk"] = { "Mawes", 6794395, "qfa-dis", -- Papuan; isolate in Glottolog, Foley (2018) and Hammarström (2010); in the Tor-Kwerba languages per -- Usher (2020) "Latn", } m["mgl"] = { "Maleu-Kilenge", 3281884, } m["mgm"] = { "Mambae", 35774, "poz-tim", "Latn", } m["mgn"] = { "Mbangi", 11017443, "nic-ngd", "Latn", } m["mgo"] = { "Meta'", 36054, "nic-mom", "Latn", } m["mgp"] = { "Eastern Magar", 12952758, "sit-gma", "Deva, Latn", } m["mgq"] = { "Malila", 6743679, "bnt-mby", "Latn", } m["mgr"] = { "Mambwe-Lungu", 626210, "bnt-mwi", "Latn", } m["mgs"] = { "Manda (Tanzania)", 16939267, "bnt-bki", } m["mgt"] = { "Mwakai", 11260674, "paa-wke", "Latn", } m["mgu"] = { "Mailu", 3278246, "paa-mal", "Latn", } m["mgv"] = { "Matengo", 6786446, "bnt-mbi", "Latn", } m["mgw"] = { "Matumbi", 6791974, "bnt-mbi", "Latn", } m["mgy"] = { "Mbunga", 6799817, "bnt-kil", } m["mgz"] = { "Mbugwe", 3426367, "bnt-mra", } m["mha"] = { "Manda (India)", 56760, "dra-kki", "Orya", translit = "kxv-translit", } m["mhb"] = { "Mahongwe", 35816, "bnt-kel", } m["mhc"] = { "Mocho", 1941682, "myn", } m["mhd"] = { "Mbugu", 36152, "qfa-mix", "Latn", ancestors = "asa", } m["mhe"] = { "Besisi", 2742262, "mkh-asl", "Latn", } m["mhf"] = { "Mamaa", 6745346, "ngf-era", "Latn", } m["mhg"] = { "Marrgu", 6772812, } m["mhi"] = { "Ma'di", 56670, "csu-mma", "Latn", strip_diacritics = {remove_diacritics = c.acute .. c.grave .. c.tilde .. c.dotbelow}, } m["mhj"] = { "Mogholi", 13336, "xgn", "Arab, Latn", translit = "fa-cls-translit", strip_diacritics = { Arab = "ar-stripdiacritics", }, } m["mhk"] = { "Mungaka", 36068, "nic-nun", } m["mhl"] = { "Mauwake", 6794095, "ngf-kum", "Latn", } m["mhm"] = { "Makhuwa-Moniga", 6900145, "bnt-mak", } m["mhn"] = { "Mòcheno", 268130, "gmw-hgm", "Latn", ancestors = "bar", sort_key = {remove_diacritics = c.grave}, } m["mho"] = { "Mashi", 10962737, "bnt-kav", "Latn", } m["mhp"] = { "Balinese Malay", 12473441, "crp", "Latn, Bali, Arab", } m["mhq"] = { "Mandan", 1957120, "sio", "Latn", } m["mhr"] = { "Eastern Mari", 3906614, "chm", "Cyrl", translit = "chm-translit", override_translit = true, strip_diacritics = {remove_diacritics = c.grave .. c.acute}, sort_key = { from = {"ё", "ҥ", "ӧ", "ӱ"}, to = {"е" .. p[1], "н" .. p[1], "о" .. p[1], "у" .. p[1]} } } m["mhs"] = { "Buru (Indonesia)", 2928650, "poz-cma", "Latn", } m["mht"] = { "Mandahuaca", 6747924, "awd-nwk", } m["mhu"] = { "Taraon", 56400, "sit-gsi", "Latn", } m["mhw"] = { "Mbukushu", 2691548, "bnt", "Latn", } m["mhx"] = { "Lhao Vo", 11149315, "tbq-brm", "Latn", } m["mhy"] = { "Ma'anyan", 2328761, "poz-bre", "Latn", } m["mhz"] = { "Mor (Austronesian)", 2122792, "poz-hce", "Latn", } m["mia"] = { "Miami", 56523, "alg", "Latn", } m["mib"] = { "Atatláhuca Mixtec", 32093046, "omq-mxt", "Latn", } m["mic"] = { "Mi'kmaq", 13321, "alg-eas", "Latn", } m["mid"] = { "Mandaic", 6991742, "sem-ase", "Mand", ancestors = "myz", translit = { Mand = "Mand-translit", }, strip_diacritics = { Mand = "Mand-stripdiacritics", } } m["mie"] = { "Ocotepec Mixtec", 25559575, "omq-mxt", "Latn", } m["mif"] = { "Mofu-Gudur", 1365132, "cdc-cbm", "Latn", } m["mig"] = { "San Miguel el Grande Mixtec", 12953719, "omq-mxt", "Latn", } m["mih"] = { "Chayuco Mixtec", 13583510, "omq-mxt", "Latn", } m["mii"] = { "Chigmecatitlán Mixtec", 12953724, "omq-mxt", "Latn", } m["mij"] = { "Mungbam", 34725, "nic-beb", "Latn", } m["mik"] = { "Mikasuki", 13316, "nai-mus", "Latn", } m["mil"] = { "Peñoles Mixtec", 42411307, "omq-mxt", "Latn", } m["mim"] = { "Alacatlatzala Mixtec", 14697894, "omq-mxt", "Latn", } m["min"] = { "Minangkabau", 13324, "poz-mly", "Latn, Arab", } m["mio"] = { "Pinotepa Nacional Mixtec", 7196415, "omq-mxt", "Latn", } m["mip"] = { "Apasco-Apoala Mixtec", 13583505, "omq-mxt", "Latn", } m["miq"] = { "Miskito", 1516803, "nai-min", "Latn", strip_diacritics = {remove_diacritics = c.circ}, } m["mir"] = { "Isthmus Mixe", 6088873, "nai-miz", "Latn", } m["mit"] = { "Southern Puebla Mixtec", 7570345, "omq-mxt", "Latn", } m["miu"] = { "Cacaloxtepec Mixtec", 12953723, "omq-mxt", "Latn", } m["miw"] = { "Akoye", 3327462, "ngf-taa", "Latn", } m["mix"] = { "Mixtepec Mixtec", 6884125, "omq-mxt", "Latn", } m["miy"] = { "Ayutla Mixtec", 13583508, "omq-mxt", "Latn", } m["miz"] = { "Coatzospan Mixtec", 3317290, "omq-mxt", "Latn", } m["mjb"] = { "Makalero", 35729, "paa-alp", "Latn", } m["mjc"] = { "San Juan Colorado Mixtec", 12953718, "omq-mxt", "Latn", } m["mjd"] = { "Northwest Maidu", 3198700, "nai-mdu", "Latn", } m["mje"] = { "Muskum", 3913334, } -- mjg "Monguor" is not recognized as a language, but it is a family code m["mji"] = { "Kim Mun", 1115317, "hmx-mie", "Latn", } m["mjj"] = { "Mawak", 11732427, "ngf-tib", "Latn", } m["mjk"] = { "Matukar", 6791963, "poz-ocw", "Latn", } m["mjl"] = { "Mandeali", 6747931, "him", "Deva, Takr", translit = "hi-translit", } m["mjm"] = { "Medebur", 6805227, "poz-ocw", "Latn", } m["mjn"] = { "Mebu", 6804364, "ngf-yup", "Latn", } m["mjo"] = { "Malankuravan", 14916887, "dra-mal", } m["mjp"] = { "Malapandaram", 10575729, "dra-tam", } m["mjq"] = { "Malaryan", 12952773, "dra-mal", } m["mjr"] = { "Malavedan", 12952775, "dra-mal", "Mlym", -- Mlym translit in [[Module:scripts/data]] } m["mjs"] = { "Miship", 3441264, "cdc-wst", "Latn", } m["mjt"] = { "Sawriya Paharia", 33907, "dra-mlo", "Beng, Deva", } m["mju"] = { "Manna-Dora", 10576453, "dra-tel", } m["mjv"] = { "Mannan", 3286037, "dra-tam", "Mlym, Taml", translit = { Taml = "ta-translit", }, -- Mlym translit in [[Module:scripts/data]] } m["mjw"] = { "Karbi", 56591, "tbq-kuk", "Latn", } m["mjx"] = { "Mahali", 12953686, "mun", } m["mjy"] = { "Mahican", 3182562, "alg-eas", "Latn", } m["mjz"] = { "Majhi", 6737786, "inc-bih", } m["mka"] = { "Mbre", 3450154, "nic", --unclassified within niger-congo tho } m["mkb"] = { "Mal Paharia", 6583595, "inc-eas", "Deva", } m["mkc"] = { "Siliput", 7515090, "paa-mam", "Latn", } m["mke"] = { "Mawchi", 21403317, } m["mkf"] = { "Miya", 43328, "cdc-wst", "Latn", } m["mkg"] = { "Mak (China)", 3280623, "qfa-kms", } m["mki"] = { "Dhatki", 32480, "raj", "Deva, Mahj, Arab", } m["mkj"] = { "Mokilese", 2335528, "poz-mic", "Latn", } m["mkk"] = { "Byep", 35052, "bnt-mka", } m["mkl"] = { "Mokole", 36047, "alv-yor", "Latn", } m["mkm"] = { "Moklen", 3319380, } m["mkn"] = { "Kupang Malay", 18458203, "crp", "Latn", } m["mko"] = { "Mingang Doso", 3915382, "alv-bwj", } m["mkp"] = { "Moikodi", 6894594, "ngf-yar", "Latn", } m["mkq"] = { "Bay Miwok", 3460957, "nai-utn", "Latn", } m["mkr"] = { "Malas", 11732402, "ngf-nad", "Latn", } m["mks"] = { "Silacayoapan Mixtec", 7514027, "omq-mxt", "Latn", } m["mkt"] = { "Vamale", 14916907, "poz-cln", "Latn", } m["mku"] = { "Konyanka Maninka", 11163298, "dmn-mnk", } m["mkv"] = { "Mav̋ea", 3073532, "poz-vnn", "Latn", } m["mkx"] = { "Cinamiguin Manobo", 12953697, "mno", "Latn", } m["mky"] = { "Taba", 3512690, "poz-hce", "Latn", } m["mkz"] = { "Makasae", 35782, "paa-eti", "Latn", } m["mla"] = { "Tamambo", 1153276, "poz-vnn", "Latn", } m["mlb"] = { "Mbule", 35843, "nic-ymb", "Latn", } m["mlc"] = { "Caolan", 3446682, "tai-cho", "Latn, Hani", sort_key = {Hani = "Hani-sortkey"}, } m["mle"] = { "Manambu", 11732406, "paa-nnd", "Latn", } m["mlf"] = { "Mal", 3281057, "mkh-khm", } m["mlh"] = { "Mape", 6753787, "ngf-kma", "Latn", } m["mli"] = { "Malimpung", 12473435, } m["mlj"] = { "Miltu", 3441310, } m["mlk"] = { "Ilwana", 6001357, "bnt-sab", } m["mll"] = { "Malua Bay", 6744946, "poz-vnc", "Latn", } m["mlm"] = { "Mulam", 3092284, "qfa-kms", "Latn", } m["mln"] = { "Malango", 3281522, "poz-sls", "Latn", } m["mlo"] = { "Mlomp", 36009, "alv-bak", } m["mlp"] = { "Bargam", 4860543, "ngf-mad", "Latn", } m["mlq"] = { "Western Maninkakan", 11028033, "dmn-wmn", } m["mlr"] = { "Vame", 3515088, "cdc-cbm", "Latn", } m["mls"] = { "Masalit", 56557, "ssa", } m["mlu"] = { "To'abaita", 36645, "poz-sls", "Latn", } m["mlv"] = { "Mwotlap", 2475538, "poz-vnn", "Latn", } m["mlw"] = { "Moloko", 1965222, "cdc-cbm", "Latn", } m["mlx"] = { "Malfaxal", 2157421, "poz-vnc", "Latn", } m["mlz"] = { "Malaynon", 18755512, "phi", } m["mma"] = { "Mama", 3913963, "nic-jrn", "Latn", } m["mmb"] = { "Momina", 6897297, } m["mmc"] = { "Michoacán Mazahua", 12953705, "oto", "Latn", } m["mmd"] = { "Maonan", 3092293, "qfa-kms", "Latn", } m["mme"] = { "Tirax", 3276286, "poz-vnc", "Latn", } m["mmf"] = { "Mundat", 56263, "cdc-wst", "Latn", } m["mmg"] = { "North Ambrym", 2842468, "poz-vnc", "Latn", } m["mmh"] = { "Mehináku", 3501838, "awd", "Latn", } m["mmi"] = { "Hember Avu", 6940113, "ngf-tib", "Latn", } m["mmj"] = { "Majhwar", 6737795, } m["mmk"] = { "Mukha-Dora", 6933447, } m["mml"] = { "Man Met", 3194984, "mkh-pal", } m["mmm"] = { "Maii", 6735599, "poz-vnc", "Latn", } m["mmn"] = { "Mamanwa", 3206623, "phi", "Latn", } m["mmo"] = { "Mangga Buang", 12952294, "poz-ocw", "Latn", } m["mmp"] = { "Musan", 2605703, "paa-amu", "Latn", } m["mmq"] = { "Aisi", 6940074, "ngf-ais", "Latn", } m["mmr"] = { "Western Xiangxi Miao", 3307901, "hmn", "Latn", } m["mmt"] = { "Malalamai", 3281496, "poz-ocw", "Latn", } m["mmu"] = { "Mmaala", 13123461, "nic-ymb", "Latn", } m["mmv"] = { "Miriti", 6873567, "sai-tuc", "Latn", } m["mmw"] = { "Emae", 3051961, "poz-pnp", "Latn", } m["mmx"] = { "Madak", 3275205, "poz-ocw", "Latn", } m["mmy"] = { "Migaama", 56259, "cdc-est", "Latn", } m["mmz"] = { "Mabaale", 11003249, "bnt-ngn", } m["mna"] = { "Mbula", 3303572, "poz-ocw", "Latn", } m["mnb"] = { "Muna", 6935584, "poz-mun", "Latn", } m["mnc"] = { "Manchu", 33638, "tuw-jrc", "mnc-Mong, Latn", ancestors = "juc", -- mnc-Mong translit in [[Module:scripts/data]] } m["mnd"] = { "Mondé", 6898840, "tup", "Latn", } m["mne"] = { "Naba", 760732, "csu-bgr", } m["mnf"] = { "Mundani", 35839, "nic-mom", "Latn", } m["mng"] = { "Eastern Mnong", 12953747, "mkh-ban", "Latn, Khmr", } m["mnh"] = { "Mono (Congo)", 33501, "bad-cnt", "Latn", } m["mni"] = { "Manipuri", 33868, "sit", "Mtei, Beng", ancestors = "omp", translit = {Mtei = "Mtei-translit"}, } m["mnj"] = { "Munji", 33639, "ira-mny", "Arab", } m["mnk"] = { "Mandinka", 33678, "dmn-wmn", "Latn, Arab, Nkoo", } m["mnl"] = { "Tiale", 6744350, "poz-vnn", "Latn", } m["mnm"] = { "Mapena", 11732415, "ngf-dag", "Latn", } m["mnn"] = { "Southern Mnong", 23857582, "mkh-ban", } m["mnp"] = { "Northern Min", 36457, "zhx-inm", "Hants", generate_alternants = "zh-generatealternants", translit = "zh-translit", sort_key = "Hani-sortkey", } m["mnq"] = { "Minriq", 2742268, "mkh-asl", "Latn", } m["mnr"] = { "Mono (California)", 33591, "azc-num", "Latn", } m["mnt"] = { "Maykulan", 3915696, "aus-pam", "Latn", } m["mnu"] = { "Mer", 6817854, "paa-mai", "Latn", } m["mnv"] = { "Rennellese", 3397346, "poz-pnp", "Latn", } m["mnw"] = { "Mon", 13349, "mkh-mnc", "Mymr", ancestors = "mkh-mmn", sort_key = { from = {"ျ", "ြ", "ွ", "ှ", "ၞ", "ၟ", "ၠ", "ၚ", "ဿ"}, to = {"္ယ", "္ရ", "္ဝ", "္ဟ", "္န", "္မ", "္လ", "င", "သ္သ"} }, } m["mnx"] = { "Sougb", 3507964, "paa-ebh", "Latn", } m["mny"] = { "Manyawa", 11002622, "bnt-mak", ancestors = "vmw", } m["mnz"] = { "Moni", 6899857, "ngf-pan", "Latn", } m["moa"] = { "Mwan", 3320111, "dmn-nbe", "Latn", } m["moc"] = { "Mocoví", 3027906, "sai-guc", "Latn", } m["mod"] = { "Mobilian", 13333, "crp", "Latn", ancestors = "cho, cic", } m["moe"] = { "Montagnais", 13351, "alg", "Latn", ancestors = "cr", strip_diacritics = {remove_diacritics = c.macron}, } m["mog"] = { "Mongondow", 3058458, "phi", "Latn", } m["moh"] = { "Mohawk", 13339, "iro-nor", "Latn", ancestors = "iro-omo", } m["moi"] = { "Mboi", 3914417, "alv-yun", } m["moj"] = { "Monzombo", 11154772, "nic-nkk", "Latn", } m["mok"] = { "Morori", 6913275, } m["mom"] = { "Mangue", 56542, "omq", "Latn", } m["moo"] = { "Monom", 6901726, "mkh-ban", } m["mop"] = { "Mopan Maya", 36183, "myn", "Latn", } m["moq"] = { "Mor (Papuan)", 11732468, "qfa-dis", -- Papuan; isolate in Glottolog and Palmer (2018); top-level TNG in Ross (2005), in Berau Gulf (under -- TNG) in Usher (2020) } m["mor"] = { "Moro", 36172, "alv-hei", "Latn", } m["mos"] = { "Moore", 36096, "nic-mre", "Latn", } m["mot"] = { "Barí", 2886281, "cba", "Latn", } m["mou"] = { "Mogum", 3440473, "cdc-est", "Latn", } m["mov"] = { "Mojave", 56510, "nai-yuc", "Latn", } m["mow"] = { "Moi (Congo)", 11124792, "bnt-bmo", "Latn", } m["mox"] = { "Molima", 3319495, "poz-ocw", "Latn", } m["moy"] = { "Shekkacho", 56827, "omv-gon", } m["moz"] = { "Mukulu", 3440403, "cdc-est", } m["mpa"] = { "Mpoto", 6928303, "bnt-mbi", "Latn", } m["mpb"] = { "Mullukmulluk", 6741120, } m["mpc"] = { "Mangarayi", 6748829, } m["mpd"] = { "Machinere", 12953681, "awd", "Latn", } m["mpe"] = { "Majang", 56724, "sdv", } m["mpg"] = { "Marba", 56614, "cdc-mas", } m["mph"] = { "Maung", 6792550, "aus-wdj", "Latn", } m["mpi"] = { "Mpade", 3280670, "cdc-cbm", "Latn", } m["mpj"] = { "Martu Wangka", 3295916, "aus-pam", "Latn", } m["mpk"] = { "Mbara (Chad)", 3912770, "cdc-cbm", } m["mpl"] = { "Middle Watut", 15887910, "poz-ocw", "Latn", } m["mpm"] = { "Yosondúa Mixtec", 12953741, "omq-mxt", "Latn", } m["mpn"] = { "Mindiri", 6863842, "poz-ocw", "Latn", } m["mpo"] = { "Miu", 6883668, "poz-ocw", "Latn", } m["mpp"] = { "Migabac", 11732448, "ngf-sop", "Latn", } m["mpq"] = { "Matís", 3299145, "sai-pan", "Latn", } m["mpr"] = { "Vangunu", 3554582, "poz-ocw", "Latn", } m["mps"] = { "Dadibi", 5208077, "paa-teb", "Latn", } m["mpt"] = { "Mian", 12952846, "ngf-msu", "Latn", } m["mpu"] = { "Makuráp", 3281037, "tup", "Latn", } m["mpv"] = { "Mungkip", 11732485, "ngf-boa", "Latn", } m["mpw"] = { "Mapidian", 6753812, "awd", "Latn", } m["mpx"] = { "Misima-Paneati", 6875666, "poz-ocw", "Latn", } m["mpy"] = { "Mapia", 3287224, "poz-mic", "Latn", } m["mpz"] = { "Mpi", 6928276, "tbq-bka", } m["mqa"] = { "Maba", 3273750, } m["mqb"] = { "Mbuko", 3502213, "cdc-cbm", "Latn", } m["mqc"] = { "Mangole", 6749097, "poz-cma", "Latn", } m["mqe"] = { "Matepi", 11732426, "ngf-han", "Latn", } m["mqf"] = { "Momuna", 6897518, } m["mqg"] = { "Kota Bangun Kutai Malay", 12952778, } m["mqh"] = { "Tlazoyaltepec Mixtec", 12953740, "omq-mxt", "Latn", } m["mqi"] = { "Mariri", 6765544, } m["mqj"] = { "Mamasa", 6745452, "poz-ssw", "Latn", } m["mqk"] = { "Rajah Kabunsuwan Manobo", 12953700, "mno", } m["mql"] = { "Mbelime", 4286473, "nic-eov", "Latn", } m["mqm"] = { "South Marquesan", 19694214, "poz-pep", "Latn", } m["mqn"] = { "Moronene", 642581, "poz-btk", "Latn", } m["mqo"] = { "Modole", 11732457, "paa-gto", "Latn", } m["mqp"] = { "Manipa", 6749799, "poz-cma", "Latn", } m["mqq"] = { "Minokok", 18642293, "poz-san", "Latn", } m["mqr"] = { "Mander", 6747979, "paa-tor", "Latn", } m["mqs"] = { "West Makian", 3033575, "paa-nha", "Latn", } m["mqt"] = { "Mok", 13018559, "mkh-pal", } m["mqu"] = { "Mandari", 3285426, "sdv-bri", } m["mqv"] = { "Mosimo", 11732478, "ngf-nwh", "Latn", } m["mqw"] = { "Murupi", 11732486, "ngf-nwh", "Latn", } m["mqx"] = { "Mamuju", 6746004, "poz-ssw", "Latn", } m["mqy"] = { "Manggarai", 3285748, "poz-cet", "Latn", } m["mqz"] = { "Malasanga", 14916889, "poz-ocw", "Latn", } m["mra"] = { "Mlabri", 3073465, "mkh", } m["mrb"] = { "Sungwadia", 3293299, "poz-vnn", "Latn", } m["mrc"] = { "Maricopa", 56386, "nai-yuc", "Latn", } m["mrd"] = { "Western Magar", 22303263, "sit-gma", "Deva", } m["mre"] = { "Martha's Vineyard Sign Language", 33494, "sgn", "Latn, Sgnw", } m["mrf"] = { "Elseng", 3915667, "qfa-unc", -- "Border or language isolate"; unclassifiable due to paucity of data "Latn", } m["mrg"] = { "Mising", 3316328, "sit-tan", "Latn, Beng, Deva", ancestors = "adi", } m["mrh"] = { "Mara Chin", 4175893, "tbq-kuk", "Latn", } m["mrj"] = { "Western Mari", 1776032, "chm", "Cyrl", translit = "chm-translit", sort_key = "mrj-sortkey", } m["mrk"] = { "Hmwaveke", 5873712, "poz-cln", "Latn", } m["mrl"] = { "Mortlockese", 3324598, "poz-mic", "Latn", } m["mrm"] = { "Mwerlap", 3331115, "poz-vnn", "Latn", } m["mrn"] = { "Cheke Holo", 2962165, "poz-ocw", "Latn", } m["mro"] = { "Mru", 1951521, "sit-mru", "Latn, Mroo", } m["mrp"] = { "Morouas", 6913299, "poz-vnn", "Latn", } m["mrq"] = { "North Marquesan", 2603808, "poz-pep", "Latn", } m["mrr"] = { "Hill Maria", 27602, "dra-mdy", "Deva", } m["mrs"] = { "Maragus", 6754640, "poz-vnc", "Latn", } m["mrt"] = { "Margi", 56241, "cdc-cbm", "Latn", } m["mru"] = { "Mono (Cameroon)", 11031964, "alv-mbm", "Latn", } m["mrv"] = { "Mangarevan", 36237, "poz-pep", "Latn", } m["mrw"] = { "Maranao", 33800, "phi", "Latn, Arab", } m["mrx"] = { "Dineor", 5278044, "paa-tor", "Latn", } m["mry"] = { "Karaga Mandaya", 6747925, "phi", } m["mrz"] = { "Marind", 6763970, "paa-mri", "Latn", } m["msb"] = { "Masbatenyo", 33948, "phi", "Latn", } m["msc"] = { "Sankaran Maninka", 11155812, "dmn-mnk", } m["msd"] = { "Yucatec Maya Sign Language", 34281, "sgn", "Latn", -- when documented } m["mse"] = { "Musey", 56328, "cdc-mas", } m["msf"] = { "Mekwei", 4544752, "paa-nim", "Latn", } m["msg"] = { "Moraid", 6909020, "paa-wbh", "Latn", } m["msi"] = { "Sabah Malay", 10867404, "crp", "Latn, Arab", } m["msj"] = { "Ma", 6720909, "nic-mbc", "Latn", } m["msk"] = { "Mansaka", 12952800, "phi", "Latn", } m["msl"] = { "Molof", 4300950, } m["msm"] = { "Agusan Manobo", 12953696, "mno", "Latn", } m["msn"] = { "Vurës", 3563857, "poz-vnn", "Latn", } m["mso"] = { "Mombum", 6897079, "ngf-mom", "Latn", } m["msp"] = { "Maritsauá", 6765915, "tup", "Latn", } m["msq"] = { "Caac", 2932212, "poz-cln", "Latn", } m["msr"] = { "Mongolian Sign Language", 3915499, "sgn", } m["mss"] = { "West Masela", 12952816, "poz-tim", "Latn", } m["msu"] = { "Musom", 6943041, "poz-ocw", "Latn", } m["msv"] = { "Maslam", 3502273, } m["msw"] = { "Mansoanka", 35814, } m["msx"] = { "Moresada", 11732475, "ngf-pom", "Latn", } m["msy"] = { "Aruamu", 3501809, "paa-rub", "Latn", } m["msz"] = { "Momare", 6897030, "ngf-sop", "Latn", } m["mta"] = { "Cotabato Manobo", 12953698, "mno", "Latn", } m["mtb"] = { "Anyin Morofo", 3502338, "alv-ctn", "Latn", ancestors = "any", } m["mtc"] = { "Munit", 11732482, "ngf-kok", "Latn", } m["mtd"] = { "Mualang", 3073458, "poz-mly", "Latn", } m["mte"] = { "Alu", 33503, "poz-ocw", "Latn", } m["mtf"] = { "Murik (New Guinea)", 7050035, "paa-lse", "Latn", } m["mtg"] = { "Una", 5580728, "ngf-mek", "Latn", } m["mth"] = { "Munggui", 6936018, "poz-hce", "Latn", } m["mti"] = { "Maiwa (New Guinea)", 6737223, "ngf-dag", "Latn", } m["mtj"] = { "Moskona", 11288953, "paa-ebh", "Latn", } m["mtk"] = { "Mbe'", 10964025, "nic-nka", "Latn", } m["mtl"] = { "Montol", 3440457, "cdc-wst", "Latn", } m["mtm"] = { "Mator", 20669419, "syd", "Cyrl", } m["mtn"] = { "Matagalpa", 3490756, "nai-min", } m["mto"] = { "Totontepec Mixe", 7828400, "nai-miz", "Latn", } m["mtp"] = { "Wichí Lhamtés Nocten", 5908756, "sai-wic", "Latn", } m["mtq"] = { "Muong", 3236789, "mkh-vie", "Latn", sort_key = "vi-sortkey", } m["mtr"] = { "Mewari", 2992857, "raj", "Deva", translit = "hi-translit", -- for now } m["mts"] = { "Yora", 3572572, "sai-pan", "Latn", } m["mtt"] = { "Mota", 3325052, "poz-vnn", "Latn", } m["mtu"] = { "Tututepec Mixtec", 7857069, "omq-mxt", "Latn", } m["mtv"] = { "Asaro'o", 3503684, "ngf-war", -- ngf-moa if we split out Molet from Asaro'o "Latn", } m["mtw"] = { "Magahat", 6729600, "phi", } m["mtx"] = { "Tidaá Mixtec", 7800805, "omq-mxt", "Latn", } m["mty"] = { "Nabi", 6956858, "paa-wpa", "Latn", } m["mua"] = { "Mundang", 36032, "alv-mbm", } m["mub"] = { "Mubi", 3440518, "cdc-est", "Latn", } m["muc"] = { "Mbu'", 35868, "nic-beb", "Latn", } m["mud"] = { "Mednyj Aleut", 1977419, "qfa-mix", ancestors = "ale, ru" } m["mue"] = { "Media Lengua", 36066, "qfa-mix", "Latn", ancestors = "es, qu", } m["mug"] = { "Musgu", 3123545, "cdc-cbm", "Latn", } m["muh"] = { "Mündü", 35981, "nic-nke", "Latn", } m["mui"] = { "Musi", 615660, "poz-mly", "Latn", } m["muj"] = { "Mabire", 3440437, } m["mul"] = { "Translingual", 7834564, "qfa-not", "All", -- NOTE: The following sort keys are used in process_page() in [[Module:headword/page]], which generates -- the default sort key for the page (corresponding to {{DEFAULTSORT:...}}) by generating a sort key for -- the pagename using `makeSortKey()` called on language object "mul". Currently this just handles -- Japanese sort keys. -- -- FIXME: This should be smarter and use the language of the page if there's only one. sort_key = { Hani = "Hani-sortkey", Jpan = "Jpan-sortkey", Hrkt = "Hira-sortkey", -- Sort all kana as Hira. Hira = "Hira-sortkey", Kana = "Hira-sortkey", }, standard_chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" .. c.punc, } m["mum"] = { "Maiwala", 12952764, "poz-ocw", "Latn", } m["muo"] = { "Nyong", 36373, "alv-lek", } m["mup"] = { "Malvi", 33413, "raj", "Deva", translit = "hi-translit" } m["muq"] = { "Eastern Xiangxi Miao", 27431376, "hmn", } m["mur"] = { "Murle", 56727, "sdv", } m["mus"] = { "Creek", 523014, "nai-mus", "Latn", } m["mut"] = { "Western Muria", 12952886, "dra-mur", } m["muu"] = { "Yaaku", 34222, "cus-eas", } m["muv"] = { "Muthuvan", 3327420, "dra-tam", } m["mux"] = { "Bo-Ung", 15831607, "ngf-hag", "Latn", } m["muy"] = { "Muyang", 3502301, "cdc-cbm", "Latn", } m["muz"] = { "Mursi", 36013, "sdv", } m["mva"] = { "Manam", 6746851, "poz-ocw", "Latn", } m["mvb"] = { "Mattole", 20824, "ath-pco", "Latn", } m["mvd"] = { "Mamboru", 578815, "poz", "Latn", } m["mvg"] = { "Yucuañe Mixtec", 25562736, "omq-mxt", "Latn", } m["mvh"] = { "Mire", 3441359, } m["mvi"] = { "Miyako", 36218, "jpx-sry", "Jpan", translit = s["jpx-translit"], display_text = s["jpx-displaytext"], strip_diacritics = s["jpx-stripdiacritics"], sort_key = s["jpx-sortkey"], } m["mvk"] = { "Mekmek", 6810592, "paa-yua", "Latn", } m["mvl"] = { "Mbara (Australia)", 6799620, "aus-pam", "Latn", } m["mvm"] = { "Muya", 2422759, "sit-qia", } m["mvn"] = { "Minaveha", 6863278, "poz-ocw", "Latn", } m["mvo"] = { "Marovo", 3294683, "poz-ocw", "Latn", } m["mvp"] = { "Duri", 3915414, "poz-ssw", "Latn", } m["mvq"] = { "Moere", 11732458, "ngf-kum", "Latn", } m["mvr"] = { "Marau", 6755069, "poz-hce", "Latn", } m["mvs"] = { "Massep", 3502895, "qfa-dis", -- poorly documented (but in vigorous use); isolate per Ethnologue, Glottolog, and Foley (2018); Greater -- Kwerba per Usher "Latn", } m["mvt"] = { "Mpotovoro", 6928305, "poz-vnc", "Latn", } m["mvu"] = { "Marfa", 713633, } m["mvv"] = { "Tagal Murut", 7675300, "poz-san", "Latn", } m["mvw"] = { "Machinga", 12952754, "bnt-rvm", } m["mvx"] = { "Meoswar", 6817777, "poz-hce", "Latn", } m["mvy"] = { "Indus Kohistani", 33399, "inc-koh", "Aran", } m["mvz"] = { "Mesqan", 6821677, "sem-eth", } m["mwa"] = { "Mwatebu", 14916896, "poz-ocw", "Latn", } m["mwb"] = { "Juwal", 6319103, "paa-mmu", "Latn", } m["mwc"] = { "Are", 29277, "poz-ocw", "Latn", } m["mwe"] = { "Mwera", 6944725, "bnt-rvm", "Latn", } m["mwf"] = { "Murrinh-Patha", 2980398, "aus-dal", "Latn", } m["mwg"] = { "Aiklep", 3399652, "poz-ocw", "Latn", } m["mwh"] = { "Mouk-Aria", 3325498, "poz-ocw", "Latn", } m["mwi"] = { "Labo", 2157452, "poz-vnc", "Latn", } m["mwk"] = { "Kita Maninkakan", 3015523, "dmn-wmn", } m["mwl"] = { "Mirandese", 13330, "roa-asl", "Latn", } m["mwm"] = { "Sar", 56850, "csu-sar", "Latn", } m["mwn"] = { "Nyamwanga", 6944666, "bnt-mwi", "Latn", } m["mwo"] = { "Sungwadaga", 3276435, "poz-vnn", "Latn", } m["mwp"] = { "Kala Lagaw Ya", 2591262, "aus-pam", "Latn", } m["mwq"] = { "Mün Chin", 331340, "tbq-kuk", } m["mwr"] = { "Marwari", 56312, "raj", "Deva, Mahj, Aran", translit = { Deva = "hi-translit", -- for now Mahj = "Mahj-translit", }, } m["mws"] = { "Mwimbi-Muthambi", 15632357, "bnt-kka", "Latn", } m["mwt"] = { "Moken", 18648701, "poz", } m["mwu"] = { "Mittu", 6883573, "csu-bbk", "Latn", } m["mwv"] = { "Mentawai", 13365, "poz-nws", "Latn", } m["mww"] = { "White Hmong", 3138829, "hmn", "Latn, Hmng, Hmnp", } m["mwz"] = { "Moingi", 11011905, } m["mxa"] = { "Northwest Oaxaca Mixtec", 12953739, "omq-mxt", "Latn", } m["mxb"] = { "Tezoatlán Mixtec", 3317286, "omq-mxt", "Latn", } m["mxd"] = { "Modang", 6888037, "poz", "Latn", } m["mxe"] = { "Mele-Fila", 3305008, "poz-pnp", "Latn", } m["mxf"] = { "Malgbe", 3502224, } m["mxg"] = { "Mbangala", 6799612, "bnt-yak", } m["mxh"] = { "Mvuba", 6944591, "csu-mle", "Latn", } m["mxi"] = { "Mozarabic", 317044, "roa-ibe", "Arab, Hebr, Latn", translit = "mxi-translit", strip_diacritics = { Arab = "ar-stripdiacritics", }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["mxj"] = { "Miju", 56332, "sit-mdz", "Latn, Deva", } m["mxk"] = { "Monumbo", 6906792, "paa-mon", "Latn", } m["mxl"] = { "Maxi Gbe", 35770, "alv-gbe", } m["mxm"] = { "Meramera", 6817936, "poz-ocw", "Latn", } m["mxn"] = { "Moi (Indonesia)", 11732459, "paa-wbh", "Latn", } m["mxo"] = { "Mbowe", 10962309, "bnt-kav", } m["mxp"] = { "Tlahuitoltepec Mixe", 7810697, } m["mxq"] = { "Juquila Mixe", 25559721, } m["mxr"] = { "Murik (Malaysia)", 3328150, nil, "Latn", } m["mxs"] = { "Huitepec Mixtec", 12953729, "omq-mxt", "Latn", } m["mxt"] = { "Jamiltepec Mixtec", 12953730, "omq-mxt", "Latn", } m["mxu"] = { "Mada (Cameroon)", 3441206, "cdc-cbm", "Latn", } m["mxv"] = { "Metlatónoc Mixtec", 36363, "omq-mxt", "Latn", } m["mxw"] = { "Namo", 12952923, "paa-nam", "Latn", } m["mxx"] = { "Mahou", 11004334, "dmn-mnk", "Latn, Nkoo", } m["mxy"] = { "Southeastern Nochixtlán Mixtec", 7070684, "omq-mxt", "Latn", } m["mxz"] = { "Central Masela", 42575433, "poz-tim", "Latn", } m["myb"] = { "Mbay", 3033565, "csu-sar", "Latn", } m["myc"] = { "Mayeka", 11129517, "bnt-boa", } m["mye"] = { "Myene", 35832, "bnt-tso", "Latn", } m["myf"] = { "Bambassi", 56540, "omv-mao", "Latn", } m["myg"] = { "Manta", 35799, "nic-mom", "Latn", } m["myh"] = { "Makah", 3280640, "wak", "Latn", } m["myj"] = { "Mangayat", 35988, "nic-ser", } m["myk"] = { "Mamara", 36187, "alv-sma", "Latn", } m["myl"] = { "Moma", 6897018, "poz", "Latn", } m["mym"] = { "Me'en", 3408516, "sdv", } m["myo"] = { "Anfillo", 34928, "omv-gon", } m["myp"] = { "Pirahã", 33825, "sai-mur", "Latn", } m["myr"] = { "Muniche", 3915654, } m["mys"] = { "Mesmes", 3508617, "sem-eth", } m["myu"] = { "Mundurukú", 746723, "tup", "Latn", } m["myv"] = { "Erzya", 29952, "urj-mdv", "Cyrl", translit = "myv-translit", override_translit = true, } m["myw"] = { "Muyuw", 3502878, "poz-ocw", "Latn", } m["myx"] = { "Masaba", 12952814, "bnt-msl", "Latn", } m["myy"] = { "Macuna", 3275059, "sai-tuc", "Latn", } m["myz"] = { "Classical Mandaic", 25559314, "sem-ase", "Mand", translit = { Mand = "Mand-translit", }, strip_diacritics = { Mand = "Mand-stripdiacritics", } } m["mza"] = { "Santa María Zacatepec Mixtec", 8063756, "omq-mxt", "Latn", } m["mzb"] = { "Northern Saharan Berber", 11156769, "ber", "Arab, Latn, Tfng", } m["mzc"] = { "Madagascar Sign Language", 12715020, "sgn", } m["mzd"] = { "Malimba", 35806, "bnt-saw", } m["mze"] = { "Morawa", 6909384, "paa-mal", "Latn", } m["mzg"] = { "Monastic Sign Language", 3217333, "sgn", } m["mzh"] = { "Wichí Lhamtés Güisnay", 7998197, "sai-wic", "Latn", } m["mzi"] = { "Ixcatlán Mazatec", 6101049, "omq-maz", "Latn", } m["mzj"] = { "Manya", 11006832, "dmn-mnk", } m["mzk"] = { "Nigeria Mambila", 11004163, "nic-mmb", "Latn", } m["mzl"] = { "Mazatlán Mixe", 25559728, } m["mzm"] = { "Mumuye", 36021, "alv-mum", "Latn", } m["mzn"] = { "Mazanderani", 13356, "ira-msh", "Arab", } m["mzo"] = { "Matipuhy", 6787588, "sai-kui", "Latn", } m["mzp"] = { "Movima", 1659701, "qfa-iso", "Latn", } m["mzq"] = { "Mori Atas", 3324070, "poz-btk", "Latn", } m["mzr"] = { "Marúbo", 3296011, "sai-pan", "Latn", } m["mzs"] = { "Macanese", 35785, "crp", "Latn", ancestors = "pt", sort_key = {Latn = {remove_diacritics = c.grave .. c.acute .. c.circ .. c.tilde .. c.diaer .. c.cedilla}}, } m["mzt"] = { "Mintil", 6869641, "mkh-asl", } m["mzu"] = { "Inapang", 6013569, "paa-tam", "Latn", } m["mzv"] = { "Manza", 36038, "gba-eas", } m["mzw"] = { "Deg", 35183, "nic-gnw", "Latn", } m["mzx"] = { "Mawayana", 6794377, "awd", } m["mzy"] = { "Mozambican Sign Language", 6927809, "sgn", } m["mzz"] = { "Maiadomu", 6735234, "poz-ocw", "Latn", } return require("Module:languages").finalizeData(m, "language") i6mwa1nuwrii5flzvjaini3jeem9cv8 Module:etymology languages/data 828 7981 54720 2026-09-25T18:10:28Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} ---------------------------------------------------------------------------------------------------------------------- --...' 54720 Scribunto text/plain local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} ---------------------------------------------------------------------------------------------------------------------- -- Afroasiatic varieties -- ---------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------- -- Berber varieties -- -------------------------------------------------------------------------------------- m["shi-med"] = { "Medieval Tashelhit", 34152, "shi", } -- Tuareg lects m["thv"] = { "Tamahaq", 56703, "tmh", } m["ttq"] = { "Tawellemmet", 56390, "tmh", } m["taq"] = { "Tamasheq", 4670066, "tmh", } m["thz"] = { "Tayert", 56388, "tmh", } m["thv-ght"] = { "Ghat", 47012900, "thv", } -------------------------------------------------------------------------------------- -- Cushitic varieties -- -------------------------------------------------------------------------------------- -- Oromo varieties m["hae"] = { "Harar Oromo", 5330355, "om", aliases = {"Eastern Oromo"}, } m["gax"] = { "Borana", 2910610, "om", aliases = {"Southern Oromo"}, } m["orc"] = { "Orma", 2919128, "om", } m["ssn"] = { "Waata", 3501553, "om", } -------------------------------------------------------------------------------------- -- Egyptian varieties -- -------------------------------------------------------------------------------------- ----------------------------------------------------- -- Ancient Egyptian varieties -- ----------------------------------------------------- m["egy-old"] = { "Old Egyptian", 447117, "egy", } m["egy-mid"] = { "Middle Egyptian", 657330, "egy", aliases = {"Classical Egyptian"}, } m["egy-nmi"] = { "Neo-Middle Egyptian", 123735278, "egy", aliases = {"Égyptien de tradition", "Traditional Egyptian"}, } m["egy-lat"] = { "Late Egyptian", 1852329, "egy", } ----------------------------------------------------- -- Coptic varieties -- ----------------------------------------------------- m["cop-akh"] = { "Akhmimic Coptic", 125176464, "cop", aliases = {"Akhmimic"}, } m["cop-boh"] = { "Bohairic Coptic", 890733, "cop", aliases = {"Bohairic", "Memphitic Coptic", "Memphitic"}, } m["cop-ggg"] = { "Coptic Dialect G", nil, "cop", aliases = {"Dialect G", "Mansuric Coptic", "Mansuric"}, } m["cop-jjj"] = { "Coptic Dialect J", nil, "cop", } m["cop-kkk"] = { "Coptic Dialect K", nil, "cop", } m["cop-ppp"] = { "Coptic Dialect P", nil, "cop", aliases = {"Proto-Theban Coptic", "Palaeo-Theban Coptic"}, } m["cop-fay"] = { "Fayyumic Coptic", 1399115, "cop", aliases = {"Fayyumic", "Faiyumic Coptic", "Faiyumic", "Fayumic Coptic", "Fayumic", "Bashmuric Coptic", "Bashmuric"}, } m["cop-her"] = { "Hermopolitan Coptic", nil, "cop", aliases = {"Hermopolitan", "Coptic Dialect H", "Ashmuninic", "Ashmuninic Coptic"}, } m["cop-lyc"] = { "Lycopolitan Coptic", nil, "cop", aliases = { "Lycopolitan", "Assiutic Coptic", "Asyutic Coptic", "Assiutic", "Asyutic", "Lyco-Diospolitan Coptic", "Lyco-Diospolitan", "Subakhmimic Coptic", "Subakhmimic" }, } m["cop-old"] = { "Old Coptic", 115518040, "cop", } m["cop-oxy"] = { "Oxyrhynchite Coptic", nil, "cop", aliases = {"Oxyrhynchite", "Mesokemic Coptic", "Mesokemic", "Middle Egyptian Coptic"}, } m["cop-ply"] = { "Proto-Lycopolitan Coptic", nil, "cop", aliases = {"Coptic Dialect i", "Proto-Lyco-Diospolitan Coptic"}, } m["cop-sah"] = { "Sahidic Coptic", 2645851, "cop", aliases = {"Sahidic", "Saidic Coptic", "Saidic", "Thebaic Coptic", "Thebaic"}, } -------------------------------------------------------------------------------------- -- Semitic varieties -- -------------------------------------------------------------------------------------- ----------------------------------------------------- -- Akkadian varieties -- ----------------------------------------------------- m["akk-old"] = { "Old Akkadian", nil, "akk", } m["akk-obb"] = { "Old Babylonian", nil, "akk", } m["akk-oas"] = { "Old Assyrian", nil, "akk", } m["akk-mbb"] = { "Middle Babylonian", nil, "akk", } m["akk-mas"] = { "Middle Assyrian", nil, "akk", } m["akk-nbb"] = { "Neo-Babylonian", nil, "akk", } m["akk-nas"] = { "Neo-Assyrian", nil, "akk", } m["akk-lbb"] = { "Late Babylonian", nil, "akk", } m["akk-stb"] = { "Standard Babylonian", nil, "akk", } ----------------------------------------------------- -- Arabic varieties -- ----------------------------------------------------- m["jrb"] = { "Judeo-Arabic", 37733, "ar", pseudo_families = "qfa-jew", } m["apc-leb"] = { "Lebanese North Levantine Arabic", 1516642, "apc", aliases = {"Lebanese Arabic"}, } m["apc-sle"] = { "South Lebanese North Levantine Arabic", 14206590, "apc", aliases = {"South Lebanese Arabic"}, } m["apc-nle"] = { "North Lebanese North Levantine Arabic", nil, "apc", aliases = {"North Lebanese Arabic"}, } m["apc-syr"] = { "Syrian North Levantine Arabic", 2143071, "apc", aliases = {"Syrian Arabic"}, } m["apc-ale"] = { "Aleppine North Levantine Arabic", 7056921, "apc-syr", aliases = {"Aleppo Arabic", "Aleppine Arabic"}, } m["apc-dam"] = { "Damascene North Levantine Arabic", 12237466, "apc-syr", aliases = {"Damascus Arabic", "Damascene Arabic"}, } m["acm-khu"] = { "Khuzestani Arabic", 1040944, "acm", } ----------------------------------------------------- -- Aramaic varieties -- ----------------------------------------------------- m["arc-bib"] = { "Biblical Aramaic", 843235, "arc", family = "sem-are", pseudo_families = "qfa-jew", } m["arc-cpa"] = { "Christian Palestinian Aramaic", 60790119, "arc", family = "sem-arw", aliases = {"Melkite Aramaic", "Palestinian Syriac", "Syropalestinian Aramaic"}, } m["arc-imp"] = { "Imperial Aramaic", 7079491, "arc", aliases = {"Official Aramaic"}, } m["arc-hat"] = { "Hatran Aramaic", 3832926, "arc", family = "sem-are", } m["arc-jla"] = { "Jewish Literary Aramaic", 105952842, "arc", pseudo_families = "qfa-jew", } m["arc-nab"] = { "Nabataean Aramaic", 36178, "arc", } m["arc-old"] = { "Old Aramaic", 3398392, "arc", } m["arc-pal"] = { "Palmyrene Aramaic", 1510113, "arc", family = "sem-arw", } m["tmr"] = { "Jewish Babylonian Aramaic", 33407, "arc", family = "sem-ase", pseudo_families = "qfa-jew", } m["jpa"] = { "Jewish Palestinian Aramaic", 948909, "arc", family = "sem-arw", aliases = {"Galilean Aramaic"}, pseudo_families = "qfa-jew", } ----------------------------------------------------- -- Hebrew varieties -- ----------------------------------------------------- m["hbo"] = { "Biblical Hebrew", 1982248, "he", aliases = {"Classical Hebrew"}, } m["he-mis"] = { "Mishnaic Hebrew", 1649362, "he", ancestors = "hbo", } m["he-med"] = { "Medieval Hebrew", 2712572, "he", ancestors = "he-mis", } m["he-IL"] = { "Israeli Hebrew", 8141, "he", } ---------------------------------------------------------------------------------------------------------------------- -- Ainu varieties -- ---------------------------------------------------------------------------------------------------------------------- m["ain-hok"] = { "Hokkaido Ainu", 20968488, "ain", aliases = {"Hokkaidō Ainu"}, } m["ain-kur"] = { "Kuril Ainu", 20967012, "ain", } m["ain-sak"] = { "Sakhalin Ainu", 20747371, "ain", } ---------------------------------------------------------------------------------------------------------------------- -- American indigenous varieties -- ---------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------- -- Inuit varieties -- ----------------------------------------------------- m["esi"] = { "North Alaskan Inupiatun", nil, "ik" } m["esk"] = { "Northwest Alaskan Inupiatun", 25559714, "ik" } ----------------------------------------------------- -- Iroquoian varieties -- ----------------------------------------------------- m["iro-ohu"] = { "Old Wendat", nil, "wdt", } m["iro-omo"] = { "Old Mohawk", nil, "moh", } m["iro-oon"] = { "Old Onondaga", nil, "ono", } ----------------------------------------------------- -- Tupi-Guarani varieties -- ----------------------------------------------------- -- Old Tupi varieties m["tpw-lga"] = { "Língua Geral Amazônica", 18275323, "tpw", aliases = {"Língua Geral"}, } m["tpw-lgp"] = { "Língua Geral Paulista", 2669239, "tpw", } ---------------------------------------------------------------------------------------------------------------------- -- Austroasiatic varieties -- ---------------------------------------------------------------------------------------------------------------------- -- Khmer varieties m["okz-ang"] = { "Angkorian Old Khmer", nil, "okz", wikipedia_article = "Khmer language#Historical periods", } m["okz-pre"] = { "Pre-Angkorian Old Khmer", nil, "okz", wikipedia_article = "Khmer language#Historical periods", } -- Central Nicobarese varieties m["ncb-cam"] = { "Camorta", 5026908, "ncb", aliases = {"Kamorta"}, } m["ncb-kat"] = { "Katchal", 17064263, "ncb", aliases = {"Tehnu"}, } m["ncb-nan"] = { "Nancowry", 6962504, "ncb", aliases = {"Nankwari"}, } ---------------------------------------------------------------------------------------------------------------------- -- Austronesian varieties -- ---------------------------------------------------------------------------------------------------------------------- -- Malay and related varieties m["ms-old"] = { -- this has the ISO code 'omy' "Old Malay", nil, "ms", } m["ms-cla"] = { "Classical Malay", nil, "ms", ancestors = "ms-old", } m["pse-bsm"] = { "Besemah", nil, "pse", } m["bew-kot"] = { "Betawi Kota", nil, "bew", aliases = {"Urban Betawi"}, -- in Jakarta } -- Philippine varieties m["xnn"] = { "Northern Kankanaey", 12953609, "kne", aliases = {"Northern Kankanay", "Northern Kankana-ey"}, } m["tl-old"] = { "Old Tagalog", 12967437, "tl", } m["tl-cls"] = { "Classical Tagalog", nil, "tl", } ---------------------------------------------------------------------------------------------------------------------- -- Caucasian varieties -- ---------------------------------------------------------------------------------------------------------------------- -- Kartvelian varieties m["ka-mid"] = { "Middle Georgian", nil, "ka", ancestors = "oge", } ---------------------------------------------------------------------------------------------------------------------- -- Dravidian varieties -- ---------------------------------------------------------------------------------------------------------------------- m["ta-mid"] = { "Middle Tamil", 20987434, "ta", } m["kn-hav"] = { "Havigannada", 24276369, "kn", } m["kn-kun"] = { "Kundagannada", 6444255, "kn", } ---------------------------------------------------------------------------------------------------------------------- -- Indo-European varieties -- ---------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------- -- Albanian varieties -- -------------------------------------------------------------------------------------- m["aln"] = { "Gheg Albanian", 181037, "sq", aliases = {"Gheg"}, } m["aae"] = { "Arbëresh Albanian", 1075302, "als", aliases = {"Arbëreshë", "Arbëresh"}, } m["aat"] = { "Arvanitika Albanian", 29347, "als", aliases = {"Arvanitika"}, } m["als"] = { "Tosk Albanian", 180937, "sq", aliases = {"Tosk"}, } -------------------------------------------------------------------------------------- -- Armenian varieties -- -------------------------------------------------------------------------------------- m["hyw"] = { "Western Armenian", 180945, "hy", } m["hye"] = { "Eastern Armenian", 181059, "hy", } -------------------------------------------------------------------------------------- -- Balto-Slavic varieties -- -------------------------------------------------------------------------------------- ------------------------------------------------------------------------- -- Baltic varieties -- ------------------------------------------------------------------------- m["bat-pro"] = { "Proto-Baltic", 1703347, "ine-bsl-pro", } m["bat-dni"] = { "Dnieper Baltic", 4163178, "bat-pro", family = "bat", aliases = {"Dniepr Baltic", "Dnieper-Oka Baltic", "Dniepr-Oka Baltic"}, } m["bat-gol"] = { "Golyad", 4142855, "bat-dni", aliases = {"Eastern Galindian", "East Galindian"}, } ------------------------------------------------------------------------- -- Slavic varieties -- ------------------------------------------------------------------------- ----------------------------------------------------- -- Bulgarian varieties -- ----------------------------------------------------- m["cu-bgm"] = { "Middle Bulgarian", 12294897, "cu", ancestors = "cu" } ----------------------------------------------------- -- Church Slavonic varieties -- ----------------------------------------------------- -- Old Moscow Church Slavonic deleted as it seems not distinct enough from Russian (Synodal) Church Slavonic. m["zls-chs-ru"] = { "Russian Church Slavonic", 21662400, "zls-chs", aliases = {"Synodal Church Slavonic", "New Moscow Church Slavonic", "Novomoskovsk Church Slavonic"}, } m["zls-chs-uk"] = { "Ukrainian Church Slavonic", 12153548, "zls-chs", aliases = {"Rusyn Church Slavonic", "Belarusian Church Slavonic"}, } --[=[ -- Not sure about these yet. m["zls-chs-bg"] = { "Bulgarian Church Slavonic", nil, "zls-chs", } m["zls-chs-cs"] = { "Czech Church Slavonic", nil, "zls-chs", } m["zls-chs-hr"] = { "Croatian Church Slavonic", nil, "zls-chs", } m["zls-chs-mk"] = { "Macedonian Church Slavonic", nil, "zls-chs", } m["zls-chs-ro"] = { "Romanian Church Slavonic", nil, "zls-chs", } m["zls-chs-sr"] = { "Serbian Church Slavonic", nil, "zls-chs", } ]=] ----------------------------------------------------- -- Czech varieties -- ----------------------------------------------------- m["cs-ear"] = { "Early Modern Czech", nil, "cs", ancestors = "zlw-ocs" } ----------------------------------------------------- -- East Slavic varieties -- ----------------------------------------------------- m["zle-ops"] = { "Old Pskovian", 4167885, "zle-ono", } m["zle-mru"] = { "Middle Russian", 35228, "ru", "Cyrs", ancestors = "orv", translit = "ru-translit", strip_diacritics = { Cyrs = { remove_diacritics = c.grave .. c.acute .. c.diaer, }, }, } m["zle-mbe"] = { "Middle Belarusian", 13211, "zle-ort", ancestors = "zle-ort", } m["zle-muk"] = { "Middle Ukrainian", 13211, "zle-ort", ancestors = "zle-ort", } m["uk-CA"] = { "Canadian Ukrainian", 4161010, "uk", } m["crp-cpr-kya"] = { "Kyakhta Chinese Pidgin Russian", 1062960, "crp-cpr", } ----------------------------------------------------- -- Polish varieties -- ----------------------------------------------------- m["zlw-mpl"] = { "Middle Polish", 402878, "pl", ancestors = "zlw-opl", strip_diacritics = { remove_diacritics = c.acute, remove_exceptions = {"Ć", "ć", "Ń", "ń", "Ó", "ó", "Ś", "ś", "Ź", "ź"}, }, } m["pl-gre"] = { "Greater Polish", 4106789, "pl", } m["pl-les"] = { "Lesser Polish", 361709, "pl", } m["pl-mas"] = { "Masovian Polish", 4274559, "pl", } m["pl-gor"] = { "Goral", 452889, "pl", } ----------------------------------------------------- -- Serbo-Croatian varieties -- ----------------------------------------------------- m["ckm"] = { "Chakavian Serbo-Croatian", 337565, "sh", aliases = {"Čakavian"}, } m["kjv"] = { "Kajkavian Serbo-Croatian", 838165, "sh", } m["sh-tor"] = { -- Linguist code srp-tor "Torlakian Serbo-Croatian", 1078803, "sh", aliases = {"Torlak"}, } -------------------------------------------------------------------------------------- -- Celtic varieties -- -------------------------------------------------------------------------------------- ----------------------------------------------------- -- Brythonic varieties -- ----------------------------------------------------- m["bry-ear"] = { "Early Brythonic", nil, "cel-bry-pro", } m["bry-lat"] = { "Late Brythonic", nil, "cel-bry-pro", } ----------------------------------------------------- -- Gaulish varieties -- ----------------------------------------------------- m["xcg"] = { "Cisalpine Gaulish", 3832927, "cel-gau", } m["xtg"] = { "Transalpine Gaulish", 29977, "cel-gau", } ----------------------------------------------------- -- Welsh varieties -- ----------------------------------------------------- m["cy-nor"] = { "North Wales Welsh", 13127692, "cy", aliases = {"North Walian Welsh", "Northern Welsh"}, } m["cy-sou"] = { "South Wales Welsh", 13127689, "cy", aliases = {"South Walian Welsh", "Southern Welsh"}, } -------------------------------------------------------------------------------------- -- Germanic varieties -- -------------------------------------------------------------------------------------- -- Proto-West Germanic varieties m["frk"] = { "Frankish", 10860505, "gmw-pro", family = "gmw-frk", aliases = {"Old Frankish"}, } m["gem-sue"] = { "Suevic", 134600275, "gmw-pro", aliases = {"Suebian"}, } m["gmw-afr-pro"] = { "Proto-Anglo-Frisian", 134603379, "gmw-nsg-pro", family = "gmw-afr", } m["gmw-nsg-pro"] = { "Proto-North Sea Germanic", 134603374, "gmw-pro", family = "gmw-nsg", aliases = {"Proto-Ingvaeonic"}, } ----------------------------------------------------- -- Dutch varieties -- ----------------------------------------------------- m["nl-BE"] = { "Belgian Dutch", 34147, "nl", aliases = {"Flemish", "Flemish Dutch", "Southern Dutch"}, } ----------------------------------------------------- -- English and Scots varieties -- ----------------------------------------------------- -- English varieties m["en-AU"] = { "Australian English", 44679, "en", } m["en-GB"] = { "British English", 7979, "en", } m["en-GB-SCT"] = { "Scottish English", 1553250, "en-GB", } m["en-GB-WLS"] = { "Welsh English", 2363292, "en-GB", } m["en-IM"] = { "Manx English", 6753295, "en-GB", } m["en-aae"] = { "Australian Aboriginal English", 783347, "en-AU", } m["en-ear"] = { "Early Modern English", 1472196, "en", ancestors = "enm", aliases = {"Early New English"}, } m["en-geo"] = { "Geordie", 653421, "en", ancestors = "enm-nor", } m["en-IE"] = { -- FIXME: "IE" doesn't cover Northern Ireland "Irish English", 665624, "en", } m["en-uls"] = { "Ulster English", 6840826, "en-IE", } m["en-GB-NIR"] = { "Northern Irish English", 6840826, -- actually the code for Ulster English "en-uls", } m["en-NNN"] = { -- NA = Namibia; NNN is NATO 3-letter code for North America "North American English", 7053766, "en", ietf_subtag = "en-021" -- 021 = UN M49 code for "Northern America" (i.e. North America wihout Central America or the Caribbean) } m["en-US"] = { "American English", 7976, "en-NNN", } m["en-NZ"] = { "New Zealand English", 44661, "en" } m["en-ZA"] = { "South African English", 1156228, "en" } m["en-US-CA"] = { "California English", 1026812, "en-US", } m["en-CA"] = { "Canadian English", 44676, "en-US", } m["en-HK"] = { "Hong Kong English", 1068863, "en", } m["en-IN"] = { "Indian English", 1348800, "en", } m["pld"] = { "Polari", 1359130, "en", } -- Scots varieties m["sco-ins"] = { "Insular Scots", 16919205, "sco", } m["sco-uls"] = { "Ulster Scots", 201966, "sco", } m["sco-nor"] = { "Northern Scots", 16928150, "sco", } m["sco-sou"] = { "Southern Scots", 7570457, "sco", aliases = {"South Scots", "Borders Scots"}, } -- Middle English varieties m["enm-esc"] = { -- Part of Middle English until it developed into Middle Scots. "Early Scots", 5326738, "enm", ancestors = "enm-nor", aliases = {"Old Scots", "Scottish Middle English"}, } m["enm-emi"] = { "East Midland Middle English", 134238810, "enm", ancestors = "ang-ang", -- Technically ang-mer, but attested Mercian is mostly WM IIRC } m["enm-ken"] = { "Kentish Middle English", 134238532, "enm", ancestors = "ang-ken", } m["enm-nor"] = { "Northern Middle English", 134238541, "enm", ancestors = "ang-nor", } m["enm-sou"] = { "Southern Middle English", 134238528, "enm", ancestors = "ang-wsx", } m["enm-wmi"] = { "West Midland Middle English", 134238824, "enm", ancestors = "ang-mer", } -- Old English varieties -- Includes both Mercian and Northumbrian. m["ang-ang"] = { "Anglian Old English", 121142917, "ang", } m["ang-ken"] = { "Kentish Old English", 11687485, "ang", } m["ang-mer"] = { "Mercian Old English", 602072, "ang-ang", } m["ang-nor"] = { "Northumbrian Old English", 1798915, "ang-ang", } m["ang-wsx"] = { "West Saxon Old English", 2658603, "ang", } ----------------------------------------------------- -- High German varieties -- ----------------------------------------------------- -- (modern) German varieties m["de-AT"] = { "Austrian German", 306626, "de", } m["de-AT-vie"] = { "Viennese German", 56474, "de-AT", } m["de-CH"] = { "Switzerland German", 1366643, "de", aliases = {"Schweizer Hochdeutsch", "Swiss Standard German", "Swiss High German"}, } m["de-bal"] = { "Baltic German", 15785413, "de", } m["de-ear"] = { "Early New High German", 1472199, "de", ancestors = "gmh", aliases = {"Early Modern High German"}, } m["ksh"] = { "Kölsch", 4624, "gmw-cfr", } m["pfl"] = { "Palatine German", 23014, "gmw-rfr", aliases = {"Pfälzisch", "Pälzisch", "Palatinate German"}, } m["sli"] = { "Silesian East Central German", 152965, "gmw-ecg", aliases = {"Silesian"}, } m["sxu"] = { "Upper Saxon German", 699284, "gmw-ecg", } -- Old High German varieties m["lng"] = { "Lombardic", 35972, "goh", } -- Alemannic German varieties m["gsw-low"] = { "Low Alemannic German", 503724, "gsw", } m["gsw-FR-als"] = { "Alsatian Alemannic German", 8786, "gsw-low", } m["gsw-hig"] = { "High Alemannic German", 503728, "gsw", } m["gsw-hst"] = { "Highest Alemannic German", 687538, "gsw", } m["wae"] = { "Walser German", 680517, "gsw-hst", } ----------------------------------------------------- -- Low German varieties -- ----------------------------------------------------- m["nds-de"] = { "German Low German", 25433, "nds", ietf_subtag = "nds-DE", -- should we make this the actual code? wikimedia_codes = "nds", } m["nds-nl"] = { "Dutch Low Saxon", 516137, "nds", ietf_subtag = "nds-NL", -- should we make this the actual code? wikimedia_codes = "nds-nl", } m["act"] = { "Achterhoeks", 153627, "nds-nl", aliases = {"Achterhoek", "Achterhooks"}, } m["drt"] = { "Drents", 2736709, "nds-nl", aliases = {"Drèents", "Dreins", "Dreints", "Drints"}, } m["frs"] = { "East Frisian Low German", 149208, "nds-de", aliases = {"East Frisian", "East Frisian Low Saxon"}, } m["gos"] = { "Gronings", 508854, "nds-nl", aliases = {"Grunnegs", "Grönnegs"}, } m["nds-lpr"] = { "Low Prussian", 33982, "nds-de", } m["sdz"] = { "Sallands", 3436668, "nds-nl", aliases = {"Sallaans", "Sallaands"}, } m["stl"] = { "Stellingwerfs", 506010, "nds-nl", aliases = {"Stellingwarfs"}, } m["twd"] = { "Twents", 497363, "nds-nl", aliases = {"Tweants"}, } m["vel"] = { "Veluws", 2484810, "nds-nl", } m["wep"] = { "Westphalian", 505655, "nds-de", aliases = {"Westfalish", "Westphalien"}, } ----------------------------------------------------- -- North Frisian varieties -- ----------------------------------------------------- m["frr-ins"] = { "Insular North Frisian", 110629610, "frr", } m["frr-fam"] = { "Föhr-Amrum North Frisian", 110629601, "frr-ins", } m["frr-foh"] = { "Föhr North Frisian", 28185, "frr-fam", aliases = {"Föhr Frisian", "Fering", "Ferring", -- Glottolog }, varieties = {"Aasdring", "Weesdring"}, } m["frr-amr"] = { "Amrum North Frisian", 28192, "frr-fam", aliases = {"Amrum Frisian", "Amrum", "Öömrang"}, } m["frr-hel"] = { "Heligoland North Frisian", 28086, "frr-ins", aliases = {"Heligoland Frisian", "Halunder", "Heligolandic Frisian", "Heligolandic North Frisian", "Helgoland Frisian", "Helgoland North Frisian", "Helgoland"}, } m["frr-syl"] = { "Sylt North Frisian", 28181, "frr-ins", aliases = {"Sylt Frisian", "Söl'ring", "Sölreng", -- Glottolog }, } m["frr-mai"] = { "Mainland North Frisian", 110629626, "frr", } m["frr-hal"] = { "Halligen North Frisian", 28177, "frr-mai", aliases = {"Halligen Frisian", "Halifreesk", "Hallingen Frisian", "Hallingen North Frisian", "Hallingen", -- Glottolog }, } m["frr-moo"] = { "Mooring North Frisian", 28187, "frr-mai", aliases = {"Bökingharde North Frisian", "Bökingharde Frisian", "Böökinghiirder frasch", "Mooring", "Moring", }, varieties = {{"East Mooring", "Ostermooring"}, {"West Mooring", "Westermooring"}}, } m["frr-kar"] = { "Karrharde North Frisian", 28191, "frr-mai", aliases = {"Karrharde Frisian", "Karrharder", }, } m["frr-goe"] = { -- Technically this refers to three adjacent dialects (Northern, Central and Southern), the latter of which went -- extinct in 1980-1981. As a result, Glottolog speaks of "Norder-Mittelgoesharde", referring to the remaining two. "Goesharde North Frisian", 28183, "frr-mai", aliases = {"Norder-Mittelgoesharde North Frisian", "Norder-Mittelgoesharde Frisian", "Norder-Mittelgoesharde", "Goesharde Frisian", "Goesharde", "Gooshiirder", }, } m["frr-wie"] = { "Wiedingharde North Frisian", 28171, "frr-mai", aliases = {"Wiedingharde Frisian", "Wiedingharde", "Wiringhiirder freesk", }, } ----------------------------------------------------- -- Old Norse varieties -- ----------------------------------------------------- m["non-grn"] = { "Greenlandic Norse", 855236, "non-own", } m["non-oen"] = { "Old East Norse", 10498031, "non", ancestors = "non", } m["non-own"] = { "Old West Norse", 2377483, "non", ancestors = "non", } ----------------------------------------------------- -- Old Swedish varieties -- ----------------------------------------------------- m["gmq-osw-lat"] = { "Late Old Swedish", 10723594, "gmq-osw", ancestors = "gmq-osw", } -------------------------------------------------------------------------------------- -- Greek varieties -- -------------------------------------------------------------------------------------- m["qsb-grc"] = { "Pre-Greek", 965052, "und", family = "qfa-sub", } m["grc-aeo"] = { "Aeolic Greek", 406373, "grc", aliases = {"Lesbic Greek", "Lesbian Greek", "Aeolian Greek"}, } m["grc-arc"] = { "Arcadian Greek", nil, "grc-arp", } m["grc-arp"] = { "Arcadocypriot Greek", 499602, "grc", } m["grc-att"] = { "Attic Greek", 506588, "grc", } m["grc-boi"] = { "Boeotian Greek", 406373, "grc-aeo", } m["grc-cyp"] = { "Cypriot Ancient Greek", -- to distinguish from Cypriot Greek below nil, "grc-arp", } m["grc-dor"] = { "Doric Greek", 285494, "grc", } m["grc-ela"] = { "Elean Greek", nil, "grc", } m["grc-epi"] = { "Epic Greek", 990062, "grc", aliases = {"Homeric Greek"}, } m["grc-ion"] = { "Ionic Greek", 504165, "grc", } m["grc-koi"] = { "Koine Greek", 107358, "grc", ancestors = "grc-att", aliases = {"Hellenistic Greek"}, } m["grc-kre"] = { "Cretan Ancient Greek", -- to distinguish from Cretan Greek below nil, "grc-dor", } m["grc-opl"] = { "Opuntian Locrian", nil, "grc", } m["grc-ozl"] = { "Ozolian Locrian", nil, "grc", } m["grc-pam"] = { "Pamphylian Greek", 2271793, "grc", } m["grc-ths"] = { "Thessalian Greek", 406373, "grc-aeo", } m["gkm"] = { "Medieval Greek", 36387, "grc", ancestors = "grc-koi", aliases = {"Byzantine Greek"}, } m["el-cyp"] = { "Cypriot Greek", 245899, "el", aliases = {"Cypriotic Greek"}, } m["el-pap"] = { "Paphian Greek", nil, "el", } m["el-crt"] = { "Cretan Greek", 588306, "el", } m["el-kth"] = { "Katharevousa", 35961, "el", "Polyt", ancestors = "gkm", aliases = {"Katharevousa Greek"}, -- Polyt display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["el-kal"] = { "Kaliarda", 12878658, "el", } -------------------------------------------------------------------------------------- -- Hittite varieties -- -------------------------------------------------------------------------------------- m["htx"] = { "Middle Hittite", 12642599, "hit", } m["nei"] = { "Neo-Hittite", 41499830, "hit", aliases = {"New Hittite"}, } m["oht"] = { "Old Hittite", 41499994, "hit", } -------------------------------------------------------------------------------------- -- Indo-Iranian varieties -- -------------------------------------------------------------------------------------- ------------------------------------------------------------------------- -- Indo-Aryan varieties -- ------------------------------------------------------------------------- -- reconstructed intermediate stages m["pra-pro"] = { "Proto-New Indo-Aryan", nil, "pra", } m["inc-ash-pro"] = { "Proto-Middle Indo-Aryan", nil, "inc-ash", } m["inc-mit"] = { "Mitanni", 1986700, "inc-pro", } m["bra-old"] = { "Old Braj", nil, "bra", } -- Apabhramsas m["inc-aav"] = { "Avahattha", nil, "inc-apa", aliases = {"Abahattha"}, } m["inc-asa"] = { "Sauraseni Apabhramsa", nil, "inc-apa", } m["inc-agu"] = { "Gurjara Apabhramsa", nil, "inc-apa", } m["inc-aka"] = { "Kasmiri Apabhramsa", nil, "inc-apa", } m["inc-ama"] = { "Maharastri Apabhramsa", nil, "inc-apa", } m["inc-ata"] = { "Takka Apabhramsa", nil, "inc-apa", } m["inc-avr"] = { "Vracada Apabhramsa", nil, "inc-apa", } -- Khortha varieties m["inc-khr-par"] = { "Parnadiya Khortha", nil, "inc-khr", } -- Assamese varieties m["as-bkm"] = { "Barpetia Kamrupi Assamese", 30642960, "as", } m["as-nkm"] = { "Nalbaria Kamrupi Assamese", 85787678, "as", } m["as-pkm"] = { "Palasbaria Kamrupi Assamese", nil, "as", } -- Bengali varieties m["bn-dvn"] = { "Dhakaiya Vaṅga Bengali", 48726851, "bn", -- Eastern Bengali variety } m["bn-nvn"] = { "Noakhailla Vaṅga Bengali", 107548681, "bn", -- Eastern Bengali variety } -- Dhivehi varieties m["dv-old"] = { "Old Dhivehi", 117790875, "dv", } m["dv-mul"] = { "Mulaku Dhivehi", nil, "dv", aliases = {"Mulaku Divehi", "Mulaku Bas"}, } m["dv-huv"] = { "Huvadhu Dhivehi", nil, "dv", aliases = {"Huvadhu Divehi", "Huvadhu Bas"}, } m["dv-add"] = { "Addu Dhivehi", nil, "dv", aliases = {"Addu Divehi", "Addu Bas"}, } -- Gujarati varieties m["gu-kat"] = { "Kathiyawadi", nil, "gu", aliases = {"Kathiyawadi Gujarati", "Kathiawadi"}, } m["gu-lda"] = { "Lisan ud-Dawat Gujarati", nil, "gu", aliases = {"Lisan ud-Dawat", "LDA"}, } -- Hindi varieties m["hi-mum"] = { "Bombay Hindi", 3543151, "hi", aliases = {"Mumbai Hindi", "Bambaiyya Hindi"}, } m["hi-mid"] = { "Middle Hindi", nil, "inc-ohi", ancestors = "inc-ohi", } -- Konkani varieties m["kok-mid"] = { "Middle Konkani", nil, "kok", aliases = {"Medieval Konkani"}, } m["kok-old"] = { "Old Konkani", nil, "kok", aliases = {"Early Konkani"}, } -- Prakrits m["pra-ard"] = { "Ardhamagadhi Prakrit", 35217, "pra", aliases = {"Ardhamagadhi"}, } m["pra-hel"] = { "Helu Prakrit", 15080869, "pra", aliases = {"Elu", "Elu Prakrit", "Helu"}, } m["pra-kha"] = { "Khasa Prakrit", nil, "pra", aliases = {"Khasa"}, } m["pra-mag"] = { "Magadhi Prakrit", -- Not to be confused with Magahi (mag) 2652214, "pra", aliases = {"Magadhi"}, } m["pra-mah"] = { "Maharastri Prakrit", 2586773, "pra", aliases = {"Maharashtri Prakrit", "Maharastri", "Maharashtri"}, } m["pra-pai"] = { "Paisaci Prakrit", 2995607, "pra-sau", aliases = {"Paisaci", "Paisachi"}, ancestors = "pra-sau" } m["pra-sau"] = { "Sauraseni Prakrit", 2452885, "pra", aliases = {"Sauraseni", "Shauraseni"}, } m["pra-ava"] = { "Avanti", nil, "pra", aliases = {"Avanti Prakrit"}, } m["pra-pra"] = { "Pracya", nil, "pra", aliases = {"Pracya Prakrit"}, } m["pra-bah"] = { "Bahliki", nil, "pra", aliases = {"Bahliki Prakrit"}, } m["pra-dak"] = { "Daksinatya", nil, "pra", aliases = {"Daksinatya Prakrit"}, } m["pra-sak"] = { "Sakari", nil, "pra", aliases = {"Sakari Prakrit"}, } m["pra-can"] = { "Candali", nil, "pra", aliases = {"Candali Prakrit"}, } m["pra-sab"] = { "Sabari", nil, "pra", aliases = {"Sabari Prakrit"}, } m["pra-abh"] = { "Abhiri", nil, "pra", aliases = {"Abhiri Prakrit"}, } m["pra-dra"] = { "Dramili", nil, "pra", aliases = {"Dramili Prakrit"}, } m["pra-odr"] = { "Odri", nil, "pra", aliases = {"Odri Prakrit"}, } -- Punjabi varieties m["pnb"] = { "Western Punjabi", 58635, "pa", "Aran", } -- Sanskrit varieties m["vsn"] = { "Vedic Sanskrit", 36858, "sa", } m["cls"] = { "Classical Sanskrit", 9333703, "sa", } m["sa-bhs"] = { "Buddhist Hybrid Sanskrit", 248758, "sa", } m["sa-bra"] = { "Brahmanic Sanskrit", 139822891, "vsn", } m["sa-epi"] = { "Epic Sanskrit", 56702805, "cls", } m["sa-neo"] = { "New Sanskrit", nil, "sa", } m["sa-rig"] = { "Rigvedic Sanskrit", 139822680, "vsn", } -- Sinhalese varieties m["si-med"] = { "Medieval Sinhalese", nil, "si", aliases = {"Medieval Sinhala"}, } ------------------------------------------------------------------------- -- Iranian varieties -- ------------------------------------------------------------------------- m["qsb-bma"] = { "the BMAC substrate", 133187435, "und", family = "qfa-sub", aliases = {"the Bactria-Margiana substrate", "the Bactria-Margiana Archaeological Complex substrate"}, } -- Historical and current Iranian dialects m["ae-old"] = { "Old Avestan", 29572, "ae", aliases = {"Gathic Avestan"}, } m["ae-yng"] = { "Younger Avestan", 29572, "ae-old", aliases = {"Young Avestan"}, } m["bcc"] = { "Southern Balochi", 33049, "bal", aliases = {"Southern Baluchi"}, } m["bgp"] = { "Eastern Balochi", 33049, "bal", aliases = {"Eastern Baluchi"}, } m["bgn"] = { "Western Balochi", 33049, "bal", aliases = {"Western Baluchi"}, } m["bsg-ban"] = { "Bandari", nil, "bsg", } m["bsg-hor"] = { "Hormozi", nil, "bsg", } m["bsg-min"] = { "Minabi", nil, "bsg", } m["kho-old"] = { "Old Khotanese", nil, "kho", } m["kho-lat"] = { "Late Khotanese", nil, "kho-old", } m["peo-ear"] = { "Early Old Persian", nil, "peo", } m["peo-lat"] = { "Late Old Persian", nil, "peo", } m["pal-ear"] = { "Early Middle Persian", nil, "pal", } m["pal-lat"] = { "Late Middle Persian", nil, "pal", ancestors = "pal-ear", } m["ps-nwe"] = { "Northwestern Pashto", nil, "ps", } m["ps-cgi"] = { "Central Ghilzay", nil, "ps-nwe", } m["ps-mah"] = { "Mahsudi", nil, "ps-nwe", } m["ps-nea"] = { "Northeastern Pashto", nil, "ps", } m["ps-afr"] = { "Afridi", nil, "ps-nea", } m["ps-bng"] = { "Bangash", nil, "ps-nea", } m["ps-xat"] = { "Khatak", nil, "ps-nea", } m["ps-pes"] = { "Peshawari", nil, "ps-nea", } m["ps-sea"] = { "Southeastern Pashto", nil, "ps", } m["ps-ban"] = { "Bannu", nil, "ps-sea", } m["ps-kak"] = { "Kakari", nil, "ps-sea", } m["ps-ser"] = { "Sher", nil, "ps-sea", } m["ps-waz"] = { "Waziri", 12274473, "ps-sea", } m["ps-swe"] = { "Southwestern Pashto", nil, "ps", } m["ps-kan"] = { "Kandahari", nil, "ps-swe", } m["ps-jad"] = { "Jadrani", nil, "ps", ancestors = "ira-pat-pro" } m["xme-azr"] = { "Old Azari", nil, "xme-ott", aliases = {"Old Azeri", "Azari", "Azeri", "Āḏarī", "Adari", "Adhari"}, } m["xme-ttc-cen"] = { "Central Tati", nil, "xme-ott", } m["xme-ttc-eas"] = { "Eastern Tati", nil, "xme-ott", } m["xme-ttc-nor"] = { "Northern Tati", nil, "xme-ott", } m["xme-ttc-sou"] = { "Southern Tati", nil, "xme-ott", } m["xme-ttc-wes"] = { "Western Tati", nil, "xme-ott", } m["xmn"] = { "Manichaean Middle Persian", nil, "pal-lat", } m["fa-ear"] = { "Early New Persian", 127413796, "fa", ancestors = "pal-lat", translit = "fa-cls-translit", } m["fa-cls"] = { "Classical Persian", 9168, "fa", ancestors = "fa-ear", translit = "fa-cls-translit", } m["fa-ira"] = { "Iranian Persian", 3513637, "fa", aliases = {"Modern Persian", "Western Persian"}, translit = "fa-ira-translit", } m["prs"] = { "Dari", 178440, "fa", aliases = {"Dari Persian", "Central Persian", "Eastern Persian", "Afghan Persian"}, translit = "fa-cls-translit", } m["haz"] = { "Hazaragi", 33398, "prs", translit = "fa-cls-translit", } m["os-dig"] = { "Digor Ossetian", 3027861, "os", aliases = {"Digoron", "Digor"}, } m["os-iro"] = { "Iron Ossetian", nil, "os", aliases = {"Iron"}, } m["sog-ear"] = { "Early Sogdian", nil, "sog", } m["sog-lat"] = { "Late Sogdian", nil, "sog-ear", } m["ro-MD"] = { "Moldovan", 36392, "ro", aliases = {"Moldavian"}, } m["oru-kan"] = { "Kaniguram", 6363164, "oru", } m["oru-log"] = { "Logar", nil, "oru", } m["oos"] = { "Old Ossetic", 65455882, "xln", } m["oos-ear"] = { "Early Old Ossetic", nil, "oos", } m["oos-lat"] = { "Late Old Ossetic", nil, "oos", } m["rdb-jir"] = { "Jirofti", nil, "rdb", } m["rdb-kah"] = { "Kahnuji", nil, "rdb", } -- Southwestern Fars lects m["fay-bur"] = { "Burenjani", nil, "fay", } m["fay-bsh"] = { "Bushehri", nil, "fay", } m["fay-dsh"] = { "Dashtaki", nil, "fay", } m["fay-dav"] = { "Davani", 5228140, "fay", } m["fay-eze"] = { "Emamzada Esmaili", nil, "fay", } m["fay-gav"] = { "Gavkoshaki", nil, "fay", } m["fay-kho"] = { "Khollari", nil, "fay", } m["fay-kon"] = { "Kondazi", nil, "fay", } m["fay-kzo"] = { "Old Kazeruni", nil, "fay", } m["fay-mas"] = { "Masarami", nil, "fay", } m["fay-pap"] = { "Papuni", nil, "fay", } m["fay-sam"] = { "Samghani", nil, "fay", } m["fay-shr"] = { "Shirazi", nil, "fay", } m["fay-sho"] = { "Old Shirazi", nil, "fay", } m["fay-kar"] = { "Khargi", nil, "fay", } m["fay-sor"] = { "Sorkhi", nil, "fay", } -- Talysh lects m["tly-cen"] = { "Central Talysh", nil, "tly", } m["tly-asa"] = { "Asalemi", nil, "tly-cen", } m["tly-kar"] = { "Karganrudi", nil, "tly-cen", } m["tly-tul"] = { "Tularudi", nil, "tly-cen", } m["tly-tal"] = { "Taleshdulabi", nil, "tly-cen", } m["tly-nor"] = { "Northern Talysh", nil, "tly", } m["tly-aze"] = { "Azerbaijani Talysh", nil, "tly-nor", } m["tly-anb"] = { "Anbarani", nil, "tly-nor", } m["tly-sou"] = { "Southern Talysh", nil, "tly", } m["tly-fum"] = { "Fumani", nil, "tly-sou", } m["tly-msu"] = { "Masulei", nil, "tly-sou", } m["tly-msa"] = { "Masali", nil, "tly-sou", } m["tly-san"] = { "Shandarmani", nil, "tly-sou", } -- Tafreshi lects m["xme-amo"] = { "Amorehi", nil, "xme-taf", } m["atn"] = { "Ashtiani", 3436590, "xme-taf", } m["xme-bor"] = { "Borujerdi", nil, "xme-taf", } m["xme-ham"] = { "Hamadani", 6302426, "xme-taf", } m["xme-kah"] = { "Kahaki", nil, "xme-taf", } m["vaf"] = { "Vafsi", 32611, "xme-taf", } -- Kermanic lects m["kfm"] = { "Khunsari", 6403030, "xme-ker", } m["xme-mah"] = { "Mahallati", nil, "xme-ker", } m["xme-von"] = { "Vonishuni", nil, "xme-ker", } m["xme-bdr"] = { "Badrudi", nil, "xme-ker", } m["xme-del"] = { "Delijani", nil, "xme-ker", } m["xme-kas"] = { "Kashani", nil, "xme-ker", } m["xme-kes"] = { "Kesehi", nil, "xme-ker", } m["xme-mey"] = { "Meymehi", nil, "xme-ker", } m["ntz"] = { "Natanzi", 6968399, "xme-ker", } m["xme-abz"] = { "Abuzeydabadi", nil, "xme-ker", } m["xme-aby"] = { "Abyanehi", nil, "xme-ker", } m["xme-far"] = { "Farizandi", nil, "xme-ker", } m["xme-jow"] = { "Jowshaqani", nil, "xme-ker", } m["xme-nas"] = { "Nashalji", nil, "xme-ker", } m["xme-qoh"] = { "Qohrudi", nil, "xme-ker", } m["xme-yar"] = { "Yarandi", nil, "xme-ker", } m["soj"] = { "Soi", 7930463, "xme-ker", aliases = {"Sohi"}, } m["xme-tar"] = { "Tari", nil, "xme-ker", } m["gzi"] = { "Gazi", 5529130, "xme-ker", } m["xme-sed"] = { "Sedehi", nil, "xme-ker", } m["xme-ard"] = { "Ardestani", nil, "xme-ker", } m["xme-zef"] = { "Zefrehi", nil, "xme-ker", } m["xme-isf"] = { "Isfahani", nil, "xme-ker", } m["xme-kaf"] = { "Kafroni", nil, "xme-ker", } m["xme-vrz"] = { "Varzenehi", nil, "xme-ker", } m["xme-xur"] = { "Khuri", nil, "xme-ker", } m["nyq"] = { "Nayini", 6983146, "xme-ker", } m["xme-ana"] = { "Anaraki", nil, "xme-ker", } m["gbz"] = { "Zoroastrian Dari", 32389, "xme-ker", aliases = {"Behdināni", "Gabri", "Gavrŭni", "Gabrōni"}, } m["xme-krm"] = { "Kermani", nil, "xme-ker", } m["xme-yaz"] = { "Yazdi", nil, "xme-ker", } m["xme-bid"] = { "Bidhandi", nil, "xme-ker", } m["xme-bij"] = { "Bijagani", nil, "xme-ker", } m["xme-cim"] = { "Chimehi", nil, "xme-ker", } m["xme-han"] = { "Hanjani", nil, "xme-ker", } m["xme-kom"] = { "Komjani", nil, "xme-ker", } m["xme-nar"] = { "Naraqi", nil, "xme-ker", } m["xme-nus"] = { "Nushabadi", nil, "xme-ker", } m["xme-qal"] = { "Qalhari", nil, "xme-ker", } m["xme-trh"] = { "Tarehi", nil, "xme-ker", } m["xme-val"] = { "Valujerdi", nil, "xme-ker", } m["xme-var"] = { "Varani", nil, "xme-ker", } m["xme-zor"] = { "Zori", nil, "xme-ker", } -- Ramandi lects m["tks-ebr"] = { "Ebrahimabadi", nil, "tks", } m["tks-sag"] = { "Sagzabadi", nil, "tks", } m["tks-esf"] = { "Esfarvarini", nil, "tks", } m["tks-tak"] = { "Takestani", nil, "tks", } m["tks-cal"] = { "Chali Tati", nil, "tks", aliases = {"Chāli"}, } m["tks-dan"] = { "Danesfani", nil, "tks", } m["tks-xia"] = { "Khiaraji", nil, "tks", } m["tks-xoz"] = { "Khoznini", nil, "tks", } -- Shughni dialects m["sgh-bro"] = { "Bartangi-Oroshori", nil, "sgh", } m["sgh-bar"] = { "Bartangi", nil, "sgh-bro", } m["sgh-oro"] = { "Oroshori", nil, "sgh-bro", aliases = {"Roshorvi"}, } m["sgh-rsx"] = { "Roshani-Khufi", nil, "sgh", } m["sgh-xuf"] = { "Khufi", 2562249, "sgh-rsx", aliases = {"Xufi", "Xūfī"}, } m["sgh-ros"] = { "Roshani", 2597566, "sgh-rsx", aliases = {"Rushani", "Rōšāni"}, } m["sgh-xgb"] = { "Khughni-Bajui", nil, "sgh", } m["sgh-xug"] = { "Khughni", nil, "sgh-xgb", } m["sgh-baj"] = { "Bajui", nil, "sgh-xgb", } ------------------------------------------------------------------------- -- Nuristani varieties -- ------------------------------------------------------------------------- m["bsh-kat"] = { "Kativiri", 2605045, "bsh", aliases = {"Katə́viri"}, } m["xvi"] = { "Kamviri", 1193495, "bsh", aliases = {"Kamvíri"}, } m["bsh-mum"] = { "Mumviri", nil, "bsh", aliases = {"Mumvíri"}, } -------------------------------------------------------------------------------------- -- Italic varieties -- -------------------------------------------------------------------------------------- ------------------------------------------------------------------------- -- Latin varieties -- ------------------------------------------------------------------------- -- Latin varieties by period m["itc-ala"] = { "Archaic Latin", nil, "la", "Latn, Ital", translit = { Ital = "Ital-translit", }, } m["itc-ola"] = { "Old Latin", 12289, "la", "Latn", ancestors = "itc-ala", } m["itc-lan"] = { "Lanuvian", 16890829, "la", aliases = {"Lanuvine"}, } m["itc-pra"] = { "Praenestine", 16889772, "la", aliases = {"Praenestinian"}, } m["la-cla"] = { "Classical Latin", 253854, "la", } m["la-vul"] = { "Vulgar Latin", 37560, "la-cla", } m["la-afr"] = { "African Romance", 162064, "roa-pro", } m["la-lat"] = { "Late Latin", 1503113, "la", ancestors = "la-cla", } m["la-med"] = { "Medieval Latin", 1163234, "la", ancestors = "la-lat", } m["la-eme"] = { "Early Medieval Latin", nil, "la-med", } m["la-ecc"] = { "Ecclesiastical Latin", 1247932, "la", aliases = {"Church Latin", "Liturgical Latin"}, ancestors = "la-lat", } m["la-ren"] = { "Renaissance Latin", 499083, "la", ancestors = "la-med", } m["la-new"] = { "New Latin", 1248221, "la", aliases = {"Modern Latin"}, ancestors = "la-ren", } m["la-con"] = { "Contemporary Latin", 1246397, "la-new", } ------------------------------------------------------------------------- -- Miscellaneous Italic varieties -- ------------------------------------------------------------------------- m["xfa-cap"] = { "Capenate", 133182969, "xfa", } m["osc-luc"] = { "Lucanian", 3265025, "osc", } m["osc-sam"] = { "Samnite", 133184287, "osc", } ------------------------------------------------------------------------- -- Romance varieties -- ------------------------------------------------------------------------- m["roa-pro"] = { "Proto-Romance", 3408029, "la-lat", ancestors = "la-vul", } ----------------------------------------------------- -- Catalan varieties -- ----------------------------------------------------- m["ca-val"] = { "Valencian", 32641, "ca", } ----------------------------------------------------- -- Franco-Provençal varieties -- ----------------------------------------------------- m["frp-old"] = { "Old Franco-Provençal", nil, "frp", } ----------------------------------------------------- -- French and derived creole varieties -- ----------------------------------------------------- m["fro-nor"] = { "Old Northern French", 2044917, "fro", aliases = {"Old Norman", "Old Norman French"}, } m["fro-pic"] = { "Picard Old French", nil, "fro", } m["xno"] = { "Anglo-Norman", 35214, "fro-nor", } m["xno-law"] = { "Law French", 2044323, "xno", } m["zrp"] = { "Zarphatic", 36994, "fro", aliases = {"Judeo-French"}, pseudo_families = "qfa-jew", } m["fr-CA"] = { "Canadian French", 1450506, "fr", } m["fr-CH"] = { "Swiss French", 1480152, "fr", } m["fr-aca"] = { "Acadian French", 415109, "fr", } m["fr-lou"] = { "Louisiana French", 3083213, "fr", } m["fr-mis"] = { "Missouri French", 3083210, "fr", } m["frc"] = { "Cajun French", 880301, "fr-lou", } m["ht-sdm"] = { "Saint Dominican Creole French", nil, "ht", ancestors = "fr", } -- Norman varieties m["nrf-grn"] = { "Guernsey Norman", 56428, "nrf", aliases = {"Guernsey"}, } m["nrf-jer"] = { "Jersey Norman", 56430, "nrf", aliases = {"Jersey"}, } ----------------------------------------------------- -- Gallo-Italic varieties -- ----------------------------------------------------- m["egl-old"] = { "Old Emilian", nil, "egl", } m["lij-old"] = { "Old Ligurian", nil, "lij", aliases = {"Old Genoese"}, } m["lmo-old"] = { "Old Lombard", 97165320, "lmo", } m["pms-old"] = { "Old Piedmontese", nil, "pms", aliases = {"Old Piemontese"}, } m["vec-old"] = { "Old Venetan", nil, "vec", aliases = {"Old Venetian"}, } m["rgn-old"] = { "Old Romagnol", nil, "rgn", } ----------------------------------------------------- -- Italo-Romance varieties -- ----------------------------------------------------- -- Italian varieties m["roa-oit"] = { "Old Italian", nil, "it", } m["it-CH"] = { "Switzerland Italian", 672147, "it", } -- Other Italo-Romance varieties m["nap-old"] = { "Old Neapolitan", nil, "nap", } m["scn-old"] = { "Old Sicilian", nil, "scn", } ----------------------------------------------------- -- Occitan varieties -- ----------------------------------------------------- m["oc-auv"] = { "Auvergnat", 35359, "oc", aliases = {"Auvernhat", "Auvergnese"}, } m["oc-gas"] = { "Gascon", 35735, "oc", } -- standardized dialect of Gascon m["oc-ara"] = { "Aranese", 10196, "oc-gas", } m["oc-lan"] = { "Languedocien", 942602, "oc", aliases = {"Lengadocian"}, } m["oc-lim"] = { "Limousin", 427614, "oc", } m["oc-pro"] = { "Provençal", 241243, "oc", aliases = {"Provencal"}, } m["oc-pro-old"] = { "Old Provençal", 2779185, "pro", } m["oc-viv"] = { "Vivaro-Alpine", 1649613, "oc", } m["oc-jud"] = { "Shuadit", 56472, "oc", aliases = { "Chouhadite", "Chouhadit", "Chouadite", "Chouadit", "Shuhadit", "Judeo-Occitan", "Judæo-Occitan", "Judaeo-Occitan", "Judeo-Provençal", "Judæo-Provençal", "Judaeo-Provençal", "Judeo-Provencal", "Judaeo-Provencal", "Judeo-Comtadin", "Judæo-Comtadin", "Judaeo-Comtadin", }, pseudo_families = "qfa-jew", } ----------------------------------------------------- -- Portuguese and derived creole varieties -- ----------------------------------------------------- -- Portuguese m["pt-BR"] = { "Brazilian Portuguese", 750553, "pt", } m["pt-PT"] = { "European Portuguese", 922399, "pt", } -- Kabuverdianu (Cape Verde Creole, Cape Verdean Creole) m["kea-bar"] = { "Barlavento Kabuverdianu", 2217638, "kea", aliases = {"Barlavento", "Barlavento Creole", "Sampadjudu"}, } m["kea-bvi"] = { "Boa Vista Kabuverdianu", 16501837, "kea-bar", aliases = {"Boa Vista Creole"}, } m["kea-sal"] = { "Sal Kabuverdianu", 18707467, "kea-bar", aliases = {"Sal Creole"}, } m["kea-saa"] = { "Santo Antão Kabuverdianu", 18707472, "kea-bar", aliases = {"Santo Antão Creole"}, } m["kea-sni"] = { "São Nicolau Kabuverdianu", 18707549, "kea-bar", aliases = {"São Nicolau Creole"}, } m["kea-svi"] = { "São Vicente Kabuverdianu", 18707550, "kea-bar", aliases = {"São Vicente Creole"}, } m["kea-sot"] = { "Sotavento Kabuverdianu", 10261559, "kea", aliases = {"Sotavento", "Sotavento Creole", "Badiu"}, } m["kea-bra"] = { "Brava Kabuverdianu", 18670181, "kea-sot", aliases = {"Brava Creole"}, } m["kea-fog"] = { "Fogo Kabuverdianu", 18706861, "kea-sot", aliases = {"Fogo Creole"}, } m["kea-mai"] = { "Maio Kabuverdianu", 18707286, "kea-sot", aliases = {"Maio Creole"}, } m["kea-san"] = { "Santiago Kabuverdianu", 35117, "kea-sot", aliases = {"Santiago Creole"}, } m["kea-alu"] = { "ALUPEC Kabuverdianu", 375704, "kea", aliases = {"ALUPEC", "Alfabeto Unificado para a Escrita do Cabo-Verdiano"}, } ----------------------------------------------------- -- Rhaeto-Romance varieties -- ----------------------------------------------------- -- Friulian varieties m["fur-old"] = { "Old Friulian", nil, "fur", } -- Ladin varieties m["lld-amp"] = { "Ampezan Ladin", 25617466, "lld", aliases = {"Anpezan", "Ampezan", "Ampezzan", "Ampezzano"}, } m["lld-bad"] = { "Badiot Ladin", 3706562, "lld", aliases = {"Badiot", "Badioto", "Badiotto"}, } m["lld-cad"] = { "Cadorino Ladin", 3706570, "lld", aliases = {"Cadorino"}, } m["lld-fas"] = { "Fascian Ladin", 742627, "lld", aliases = {"Fascian", "Fassano"}, } m["lld-fod"] = { "Fodom Ladin", 3706605, "lld", aliases = {"Fodom", "Livinallese"}, } m["lld-for"] = { "Fornes Ladin", 5470374, "lld", aliases = {"Fornes"}, } m["lld-ghe"] = { "Gherdëina Ladin", 3706597, "lld", aliases = {"Gherdëina", "Gardenese", "Val Gardena"}, } m["lld-non"] = { "Nones Ladin", 1055027, "lld", aliases = {"Nones", "Noneso"}, } -- Romansh varieties m["rm-old"] = { "Old Romansh", nil, "rm", } m["rm-put"] = { "Puter Romansh", 688309, "rm", aliases = {"Puter", "Putèr", "Upper Engadine", "rm-puter"}, } m["rm-srm"] = { "Surmiran Romansh", 690216, "rm", aliases = {"Surmiran", "rm-surmiran", "Surmiran-Albula", -- Glottolog }, } m["rm-srs"] = { "Sursilvan Romansh", 688348, "rm", aliases = {"Sursilvan", "rm-sursilv"}, } m["rm-sut"] = { "Sutsilvan Romansh", 688272, "rm", aliases = {"Sutsilvan", "rm-sutsilv"}, varieties = {"Scharans Sutsilvan"}, -- per Glottolog } m["rm-val"] = { "Vallader Romansh", 690226, "rm", aliases = {"Vallader", "Putèr", "Lower Engadine", "rm-vallader"}, } m["rm-gri"] = { "Rumantsch Grischun", 688873, "rm", aliases = {"rm-rumgr"}, } ----------------------------------------------------- -- Sardinian varieties -- ----------------------------------------------------- m["sc-old"] = { "Old Sardinian", nil, "sc", } m["sc-src"] = { "Logudorese", 777974, "sc", aliases = {"Logudorese Sardinian"}, } m["sc-nuo"] = { "Nuorese", nil, "sc-src", aliases = {"Nuorese Sardinian"}, } m["sc-sro"] = { "Campidanese", 35348, "sc", aliases = {"Campidanese Sardinian"}, } ----------------------------------------------------- -- Spanish varieties -- ----------------------------------------------------- m["es-ear"] = { "Early Modern Spanish", 5364419, "es", } m["es-AR"] = { "Rioplatense Spanish", 509780, "es", } m["es-BO"] = { "Bolivian Spanish", 510730, "es", } m["es-CL"] = { "Chilean Spanish", 857295, "es", } m["es-CO"] = { "Colombian Spanish", 1115875, "es", } m["es-CU"] = { "Cuban Spanish", 824909, "es", } m["es-MX"] = { "Mexican Spanish", 616620, "es", } m["es-PE"] = { "Peruvian Spanish", 736236, "es", } m["es-PH"] = { "Philippine Spanish", 22091406, "es", } m["es-US"] = { "United States Spanish", 2301077, "es", aliases = {"US Spanish"}, } --use label "US Spanish" to put Spanish terms in this category m["es-PR"] = { "Puerto Rican Spanish", 7258609, "es", } m["es-VE"] = { "Venezuelan Spanish", 840017, "es", } m["es-lun"] = { "Lunfardo", 1401612, "es", } ---------------------------------------------------------------------------------------------------------------------- -- Japonic varieties -- ---------------------------------------------------------------------------------------------------------------------- -- Japanese varieties m["ja-mid"] = { "Middle Japanese", 6841474, "ojp", ancestors = "ojp", } m["ja-mid-ear"] = { "Early Middle Japanese", 182695, "ja-mid", } m["ja-mid-lat"] = { "Late Middle Japanese", 1816184, "ja-mid", ancestors = "ja-mid-ear", } m["ja-ear"] = { "Early Modern Japanese", 5326692, "ja", ancestors = "ja-mid-lat", } m["ojp-eas"] = { "Eastern Old Japanese", 65247957, "ojp", } m["ja-cla"] = { "Classical Japanese", 1332057, "ja", -- FIXME: This is redundant because Classical Japanese is considered a child of (Modern) Japanese, which has ja-mid-ear -- (as well as ja-mid-lat) as ancestors. However, the intent here is that the *direct* ancestor of ja-cla is ja-mid-ear -- and ja-mid-lat is not an ancestor. Need to rethink ancestor handling. -- ancestors = "ja-mid-ear", } ---------------------------------------------------------------------------------------------------------------------- -- Koreanic varieties -- ---------------------------------------------------------------------------------------------------------------------- -- Korean varieties m["ko-KR"] = { "South Korean standard", 18795, "ko", aliases = {"South Korean","Pyojuneo","P'yojunŏ"}, } m["ko-KP"] = { "North Korean standard", 18784, "ko", aliases = {"North Korean","Munhwa'eo","Munhwa'ŏ"}, } m["oko-lat"] = { "Late Old Korean", nil, "oko", } m["okm-ear"] = { "Early Middle Korean", nil, "okm", } m["ko-cen"] = { "Central Korean", nil, "ko", } m["ko-gyg"] = { "Gyeonggi Korean", 485492, "ko-cen", aliases = {"Seoul Korean"}, } m["ko-chu"] = { "Chungcheong Korean", 625800, "ko-cen", aliases = {"Hoseo Korean"}, } m["ko-hwa"] = { "Hwanghae Korean", 16183706, "ko-cen", } m["ko-gan"] = { "Gangwon Korean", 11260444, "ko-cen", aliases = {"Yeongdong Korean"}, } m["ko-gys"] = { "Gyeongsang Korean", 488002, "ko", aliases = {"Southeastern Korean"}, } m["ko-jeo"] = { "Jeolla Korean", 11250166, "ko", aliases = {"Southwestern Korean"}, } m["ko-pyo"] = { "Pyongan Korean", 7263142, "ko", aliases = {"Northwestern Korean"}, } m["ko-ham"] = { "Hamgyong Korean", 860702, "ko", aliases = {"Northeastern Korean"}, } m["ko-yuk"] = { "Yukjin Korean", 16171275, "ko", aliases = {"Yukchin Korean", "Ryukjin Korean", "Ryukchin Korean"}, } ---------------------------------------------------------------------------------------------------------------------- -- Mongolic varieties -- ---------------------------------------------------------------------------------------------------------------------- m["xng-ear"] = { "Early Middle Mongol", nil, "xng", } m["xng-lat"] = { "Late Middle Mongol", nil, "xng", ancestors = "xng-ear", } m["mn-kha"] = { "Khalkha Mongolian", 6399808, "mn", aliases = {"Khalkha"}, } m["mn-ord"] = { "Ordos Mongolian", 716904, "mn", aliases = {"Ordos"}, } m["mn-cha"] = { "Chakhar Mongolian", 907425, "mn", aliases = {"Chakhar"}, } m["mn-khr"] = { "Khorchin Mongolian", 3196210, "mn", aliases = {"Khorchin"}, } ---------------------------------------------------------------------------------------------------------------------- -- Niger-Congo varieties -- ---------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------- -- Akan varieties -- ----------------------------------------------------- m["tw"] = { "Twi Akan", 36850, "ak", aliases = {"Twi"}, } m["abr"] = { "Abron", 34831, "tw", aliases = {"Brong", "Bono", "Bono Twi"}, } m["tw-asa"] = { "Asante Twi", 19261685, "tw", aliases = {"Asante", "Ashanti", "Ashante"}, } m["tw-aku"] = { "Akuapem Twi", 31150449, "tw", aliases = {"Akuapem", "Akuapim", "Akwapem Twi", "Akwapi"}, } m["fat"] = { "Fante Akan", 35570, "ak", aliases = {"Fante", "Fanti", "Fantse", "Mfantse"}, } m["wss"] = { "Wasa", 36914, "ak", } -- creole m["alv-kro"] = { "Kromanti", 1093206, "crp-mar", } ----------------------------------------------------- -- Bantu varieties -- ----------------------------------------------------- m["bnt-cmn"] = { "Common Bantu", nil, "bnt-pro", } m["xh-bha"] = { "Bhaca", 4900493, "xh", aliases = {"isiBhaca", "IsiBhaca"}, } -- Rwanda-Rundi varieties m["rw-kin"] = { "Kinyarwanda", 33573, "rw", aliases = {"Rwanda"}, } m["rw-run"] = { "Kirundi", 33583, "rw", aliases = {"Rundi"}, } ----------------------------------------------------- -- Fula varieties -- ----------------------------------------------------- m["fuc"] = { "Pulaar", 1420205, "ff", } m["fuf"] = { "Pular", 3915357, "ff", } m["ffm"] = { "Maasina Fulfulde", 3915322, "ff", } m["fue"] = { -- no enwiki entry as of yet but frwiki and pmswiki have one "Borgu Fulfulde", 12952426, "ff", } m["fuh"] = { -- no enwiki entry as of yet but frwiki and pmswiki have one "Western Niger Fulfulde", 12952430, "ff", } m["fuq"] = { -- no enwiki entry as of yet but frwiki, hrwiki and pmswiki have one "Central-Eastern Niger Fulfulde", 12628799, "ff", } m["fuv"] = { -- no enwiki entry as of yet but dewiki, frwiki, hrwiki, pmswiki and swwiki have one "Nigerian Fulfulde", 36129, "ff", } m["fub"] = { -- no enwiki entry as of yet but dewiki, frwiki, hrwiki, pmswiki, ptwiki, swwiki and yowiki have one "Adamawa Fulfulde", 34776, "ff", } m["fui"] = { -- no enwiki entry as of yet but pmswiki and swwiki have one "Bagirmi Fulfulde", 11003859, "ff", } ---------------------------------------------------------------------------------------------------------------------- -- Papuan varieties -- ---------------------------------------------------------------------------------------------------------------------- m["kze"] = { "Kosena", 12952663, "auy", } m["ont"] = { "Ontenu", 3352827, "gaj", aliases = {"Ontena"}, } ---------------------------------------------------------------------------------------------------------------------- -- Salishan varieties -- ---------------------------------------------------------------------------------------------------------------------- m["lut-nor"] = { "Northern Lushootseed", nil, "lut", aliases = {"Northern Puget Sound Salish"}, } m["slh"] = { "Southern Lushootseed", 7997684, "lut", aliases = {"Southern Puget Sound Salish", "Twulshootseed", "Whulshootseed"}, } m["ska"] = { "Skagit", 12642471, "lut-nor", } m["sno"] = { "Snohomish", 25559662, "lut-nor", } ---------------------------------------------------------------------------------------------------------------------- -- Sino-Tibetan varieties -- ---------------------------------------------------------------------------------------------------------------------- m["tbq-pro"] = { "Proto-Tibeto-Burman", 7251864, "sit-pro", } ----------------------------------------------------- -- Chinese varieties -- ----------------------------------------------------- ------------- Old Chinese, Middle Chinese ------------- m["och-ear"] = { "Early Old Chinese", nil, "och", } m["och-lat"] = { "Late Old Chinese", nil, "och", } m["ltc-ear"] = { "Early Middle Chinese", nil, "ltc", } m["ltc-lat"] = { "Late Middle Chinese", nil, "ltc", } ------------- Classical/Literary varieties ------------- -- FIXME: Temporary. m["lzh-shi"] = { "Traditional Chinese poetry", 1759242, "lzh", } -- FIXME: Temporary. m["lzh-cii"] = { "Ci", 1091366, "lzh", } -- FIXME: Temporary. m["lzh-yue"] = { "Classical Cantonese", nil, "lzh", } -- FIXME: Temporary. m["lzh-cmn"] = { "Classical Mandarin", nil, "lzh", } -- FIXME: Temporary. m["lzh-tai"] = { "Classical Taishanese", nil, "lzh", } -- FIXME: Temporary. m["lzh-cmn-TW"] = { "Classical Taiwanese Mandarin", nil, "lzh-cmn", } -- FIXME: Temporary. m["lzh-VI"] = { "Vietnamese Classical Chinese", 17034227, "lzh", } -- FIXME: Temporary. m["lzh-KO"] = { "Korean Classical Chinese", 10496257, "lzh", ietf_subtag = "lzh-KR" -- KR = South Korea, as there is no code for Korea as a whole } -- FIXME: Temporary. m["lzh-lit"] = { "Literary Chinese", nil, "lzh", } -- FIXME: Temporary. FIXME: Do we need this? How does it differ from Old Chinese? m["lzh-pre"] = { "Pre-Classical Chinese", nil, "lzh", } ------------- Written Vernacular varieties ------------- -- FIXME: Temporary. m["cmn-wvc"] = { "Written vernacular Mandarin", 783605, "cmn", } -- FIXME: Temporary. FIXME: How does this differ from "Literary Cantonese"? m["yue-wvc"] = { "Written vernacular Cantonese", nil, "yue", } -- FIXME: Temporary. m["zhx-tai-wvc"] = { "Written vernacular Taishanese", nil, "zhx-tai", } ------------- Mandarin varieties ------------- -- FIXME: Temporary. NOTE: The Linguist List assigns the "w:Beijing dialect" (Wikidata 1147606) the code "cmn-bej" and -- the larger "w:Beijing Mandarin (division of Mandarin)" dialect group (Wikidata 2169652; what we call "Beijingic -- Mandarin", after Glottolog) the code "cmn-bei". m["cmn-bei"] = { "Beijing Mandarin", 1147606, "cmn-bec", } -- FIXME: Temporary. m["cmn-bec"] = { "Beijingic Mandarin", 2169652, "cmn", } -- FIXME: Temporary. NOTE: The Linguist List uses the code cmn-zho. m["cmn-cep"] = { "Central Plains Mandarin", 3048775, "cmn", aliases = {"Zhongyuan Mandarin"}, } m["cmn-ear"] = { "Early Mandarin", 837169, "cmn", ancestors = "ltc", } -- FIXME: Temporary. m["cmn-gua"] = { "Guanzhong Mandarin", 3431648, "cmn-cep", } -- FIXME: Temporary. Appears to be a subdialect of Guiliu Mandarin, which in turn is a subdialect of Southwestern Mandarin. m["cmn-gui"] = { "Guilin Mandarin", 11111636, "cmn-sow", } m["cmn-jhu"] = { "Jianghuai Mandarin", 2128953, "cmn", aliases = {"Lower Yangtze Mandarin"}, } -- FIXME: Temporary. m["cmn-lan"] = { "Lanyin Mandarin", 662754, "cmn", } -- FIXME: Temporary. m["cmn-MY"] = { "Malaysian Mandarin", 13646143, "cmn", } -- FIXME: Temporary. m["cmn-nan"] = { "Nanjing Mandarin", 2681098, "cmn-jhu", } -- FIXME: Temporary. m["cmn-noe"] = { "Northeastern Mandarin", 1064504, "cmn", } -- FIXME: Temporary. m["cmn-PH"] = { "Philippine Mandarin", 7185155, "cmn", } -- FIXME: Temporary. m["cmn-SG"] = { "Singapore Mandarin", 1048980, "cmn", } -- FIXME: Temporary. m["cmn-sow"] = { "Southwestern Mandarin", 2609239, "cmn", } -- FIXME: Temporary. Appears to be a subdialect of Jilu Mandarin. m["cmn-tia"] = { "Tianjin Mandarin", 7800220, "cmn", } -- FIXME: Temporary. NOTE: Wikidata also has Q4380827 "Taiwanese Mandarin", defined as "rare dialect of Standard Chinese -- (Mandarin) used in Taiwan, which is strongly influenced by Taiwanese Hokkien; mostly used by elderlies" and having no -- English Wikipedia article (but see w:zh:臺灣國語). m["cmn-TW"] = { "Taiwanese Mandarin", 262828, "cmn", } -- FIXME: Temporary. Appears to be a subdialect of Wu-Tian Mandarin, in turn a subdialect of Southwestern Mandarin. -- Given the code cmn-xwu in the Linguist List. m["cmn-wuh"] = { "Wuhan Mandarin", 11124731, "cmn-sow", aliases = {"Wuhanese"}, } -- FIXME: Temporary. Appears to be a subdialect of Lanyin Mandarin. m["cmn-xin"] = { "Xining Mandarin", nil, "cmn-lan", } -- FIXME: Temporary. m["cmn-yan"] = { "Yangzhou Mandarin", nil, "cmn-jhu", } ------------- Cantonese varieties ------------- -- FIXME: Temporary. m["yue-gua"] = { "Guangzhou Cantonese", nil, "yue", } -- FIXME: Temporary. Given the codes yue-yue or yue-can in the Linguist List. m["yue-HK"] = { "Hong Kong Cantonese", 5894342, "yue", } -- FIXME: Temporary. FIXME: How does this differ from "Written vernacular Cantonese"? m["yue-lit"] = { "Literary Cantonese", 2472605, "yue", } ------------- Wu varieties ------------- m["wuu-han"] = { "Hangzhounese", 5648144, "wuu", } m["wuu-nin"] = { "Ningbonese", 3972199, "wuu", } -- FIXME: Temporary. m["wuu-nor"] = { "Northern Wu", 7675988, "wuu", aliases = {"Taihu Wu"}, } -- FIXME: Temporary? Subvariety of Taihu Wu. NOTE: "chm" stands for Chongming, the main dialect, to avoid a conflict -- with Shanghainese. m["wuu-chm"] = { "Shadi Wu", 6112340, "wuu-nor", } m["wuu-sha"] = { "Shanghainese", 36718, "wuu-nor", } m["wuu-suz"] = { "Suzhounese", 831744, "wuu-nor", } -- FIXME: Temporary. May be converted into a full language and/or split. m["wuu-wen"] = { "Wenzhounese", 710218, "wuu", } ------------- Xiang varieties ------------- m["hsn-lou"] = { "Loudi Xiang", 10943823, "hsn-old", } m["hsn-hya"] = { "Hengyang Xiang", 20689035, "hsn-hzh", } m["hsn-hzh"] = { "Hengzhou Xiang", nil, "hsn", } m["hsn-new"] = { "New Xiang", 7012696, "hsn", aliases = {"Chang-Yi"}, } m["hsn-old"] = { "Old Xiang", 7085453, "hsn", aliases = {"Lou-Shao"}, } ------------- Hakka varieties ------------- -- FIXME: Temporary. m["hak-dab"] = { "Dabu Hakka", 19855566, "hak", -- formerly hak-TW but seems to be spoken primary in Dabu County in Guangdong } -- FIXME: Temporary. m["hak-eam"] = { "Early Modern Hakka", nil, "hak", } -- FIXME: Temporary. m["hak-hai"] = { "Hailu Hakka", 17038519, "hak", -- often considered a Taiwanese lect but also spoken in [[Shanwei]], [[Guangdong]] } -- FIXME: Temporary. m["hak-HK"] = { "Hong Kong Hakka", 2675834, "hak", } -- FIXME: Temporary. m["hak-hui"] = { "Huiyang Hakka", 16873881, "hak", } -- FIXME: Temporary. m["hak-hui-MY"] = { "Malaysian Huiyang Hakka", nil, "hak-hui", } -- FIXME: Temporary. Similar to and possibly the parent of Sixian Hakka in Taiwan. m["hak-mei"] = { "Meixian Hakka", 839295, "hak", aliases = {"Moiyan Hakka", "Meizhou Hakka"}, } -- FIXME: Temporary. m["hak-six"] = { "Sixian Hakka", 9668261, "hak-TW", } -- FIXME: Temporary. m["hak-TW"] = { "Taiwanese Hakka", 2391532, "hak", } -- FIXME: Temporary. m["hak-zha"] = { "Zhao'an Hakka", 6703311, "hak", aliases = {"Zhangzhou Hakka"}, } -- Southern Min varieties -- m["nan-anx"] = { "Anxi Hokkien", 97064149, "nan-qua", } m["nan-cha"] = { "Changtai Hokkien", nil, "nan-zha", } m["nan-hou"] = { "Houlu Min", 19855492, "nan-dat", } m["nan-hui"] = { "Hui'an Hokkien", 16241797, "nan-qua", } m["nan-jin"] = { "Jinjiang Hokkien", 11089375, "nan-qua", } m["nan-kin"] = { "Kinmenese Hokkien", 56278342, "nan-xia", aliases = {"Kinmen Hokkien"}, } m["nan-med"] = { "Medan Hokkien", 6805114, "nan-zha", } m["nan-pen"] = { "Penang Hokkien", 11120689, "nan-zha", } m["nan-hbl-PH"] = { "Philippine Hokkien", 3236692, "nan-qua", } m["nan-qia"] = { "Qianlu Min", 19842517, "nan-dat", } m["nan-qua"] = { "Quanzhou Hokkien", 2251677, "nan-hbl", aliases = {"Chinchew", "Choanchew"}, } -- FIXME: Temporary? Derived from both Quanzhou and Zhangzhou Hokkien. m["nan-hbl-SG"] = { "Singapore Hokkien", 3846528, "nan-hbl", } m["nan-spm"] = { "Southern Malaysian Hokkien", 7570322, "nan-qua", aliases = {"Southern Malaysia Hokkien", "Southern Peninsular Malaysian Hokkien", "Southern Peninsular Malaysia Hokkien"} } m["nan-hbl-TW"] = { "Taiwanese Hokkien", 36778, "nan-hbl", } m["nan-ton"] = { "Tong'an Hokkien", nil, "nan-xia", } m["nan-xia"] = { "Xiamen Hokkien", 68744, "nan-hbl", aliases = {"Amoy", "Amoyese", "Amoynese", "Xiamenese"}, } m["nan-yon"] = { "Yongchun Hokkien", 65118728, "nan-qua", } m["nan-zha"] = { "Zhangzhou Hokkien", 8070492, "nan-hbl", aliases = {"Changchew", "Chiangchew", "Changchow"}, } m["nan-zho"] = { "Zhao'an Hokkien", 65118728, "nan-zha", aliases = {"Zhao'an", "Chawan", "Chawan Hokkien"}, } m["nan-zhp"] = { "Zhangping Hokkien", 15937822, "nan-zha", } ------------- Other Min varieties ------------- -- FIXME: Temporary. Affiliation within Min uncertain; some combination of Eastern and Southern. m["zhx-zho"] = { "Zhongshan Min", 8070958, "zh", } ------------- Other Chinese varieties ------------- -- FIXME: Temporary. Affiliation within Chinese uncertain; possibly Yue. m["zhx-dan"] = { "Danzhou Chinese", 2578935, "zh", } ------------- Chinese romanization varieties ------------- -- [[Wiktionary:Information desk/2022/June#Etymology Coding Issue]] -- [[Wiktionary:Grease pit/2022/June#Transliteration Systems in Etymologies 2]] m["cmn-pinyin"] = { "Hanyu Pinyin", 42222, "cmn", aliases = {"Pinyin"}, } m["cmn-tongyong"] = { "Tongyong Pinyin", 700739, "cmn", } m["cmn-wadegiles"] = { "Wade–Giles", 208442, "cmn", aliases = {"Wade-Giles", "Wade Giles"}, } m["zh-postal"] = { "Postal Romanization", 151868, "zh", } -- Chinese cyrillization m["cmn-palladius"] = { "Palladius", 1234239, "cmn", aliases = {"Palladius system"}, } ----------------------------------------------------- -- Tibetic varieties -- ----------------------------------------------------- m["adx"] = { "Amdo Tibetan", 56509, "bo", } m["kbg"] = { "Khamba", 12952626, "bo", } m["khg"] = { "Khams Tibetan", 56601, "bo", } m["tsk"] = { "Tseku", 11159532, "bo", } ---------------------------------------------------------------------------------------------------------------------- -- Tai-Kadai varieties -- ---------------------------------------------------------------------------------------------------------------------- m["th-old"] = { "Old Thai", nil, "tai-swe-pro", wikipedia_article = "Thai language#Old Thai", } m["th-suk"] = { "Sukhothai Old Thai", -- 1238-1438. Cannot use "Sukhothai Thai" as Sukhothai is the current city and "Sukhothai Thai" is a lect. nil, "th-old", aliases = {"Sukhothai Siamese"}, wikipedia_article = "Thai language#Old Thai", } m["th-ayu"] = { "Ayutthaya Old Thai", -- 1351-1767. Cannot use "Ayutthaya Thai" as Ayutthaya is the current city. nil, "th-old", aliases = {"Ayutthaya Siamese"}, wikipedia_article = "Thai language#Old Thai", } --[[ m["th-new"] = { "Hacked Thai", -- temporary for testing new translit/display methods nil, "th", translit = "User:Benwing2/th-scraping-translit", display_text = "User:Benwing2/th-scraping-translit", strip_diacritics = "User:Benwing2/th-scraping-translit", preprocess_links = "User:Benwing2/th-scraping-translit", } ]] m["tai-shz"] = { "Shangsi Zhuang", 13216, "za", } ---------------------------------------------------------------------------------------------------------------------- -- Turkic varieties -- ---------------------------------------------------------------------------------------------------------------------- m["trk-cmn-pro"] = { "Proto-Common Turkic", 1126028, "trk-pro", } m["trk-ogr-pro"] = { "Proto-Oghur", 1422731, "trk-pro", family = "trk-ogr", } m["trk-bul-pro"] = { "Proto-Bulgar", nil, "trk-ogr-pro", } m["trk-ogz-pro"] = { "Proto-Oghuz", 494600, "trk-pro", family = "trk-ogz", aliases = {"Southwestern Common Turkic"}, } m["crh-dbj"] = { "Dobrujan Tatar", 12811566, "crh", aliases = {"Romanian Tatar"}, } m["cv-ana"] = { "Anatri Chuvash", nil, "cv", aliases = {"Anatri", "Lower Chuvash"}, } m["cv-mid"] = { "Middle Chuvash", nil, "cv", ancestors = "cv-old", } m["cv-old"] = { "Old Chuvash", nil, "cv", ancestors = "xbo-vol", } m["cv-vir"] = { "Viryal Chuvash", 4278332, "cv", aliases = {"Viryal", "Upper Chuvash"}, } m["kjh-fyu"] = { "Fuyu Kyrgyz", 2598963, "kjh", aliases = {"Fuyu Kirgiz", "Fuyu Kirghiz", "Manchurian Kyrgyz", "Manchurian Kirgiz", "Manchurian Kirghiz"}, } m["klj-arg"] = { "Arghu", 33455, "klj", ancestors = "trk-cmn-pro", } m["otk-kir"] = { "Old Kirghiz", 83142, "otk", aliases = {"Yenisei Turkic", "Yenisei Kyrgyz"}, } m["otk-ork"] = { "Orkhon Turkic", 31295480, "otk", } m["qwm-cum"] = { "Cuman", 1075050, "qwm", aliases = {"Kuman", "Polovtsian", "Polovcian"}, } m["qwm-arm"] = { "Armeno-Kipchak", 2027503, "qwm", ancestors = "qwm-cum", aliases = {"Xıpçaχ tili", "Tatarça"}, } m["qwm-mam"] = { "Mamluk-Kipchak", 4279942, "qwm", aliases = {"Mameluk-Kipchak"}, } m["az-cls"] = { "Classical Azerbaijani", nil, "az", aliases = {"Classical Azeri"}, } m["qxq"] = { "Qashqai", 13192, "az", aliases = {"Qaşqay", "Qashqayi", "Kashkai", "Kashkay"}, } m["tr-CY"] = { "Cypriot Turkish", 7917392, "tr", } m["uz-afg"] = { -- NOTE: has ISO 639-3 code uzs assigned to it. "Afghan Uzbek", 1066787, "uz", aliases = {"Southern Uzbek"}, translit = "uz-afg-translit", } m["xbo-dan"] = { "Danube Bulgar", nil, "xbo", } m["xbo-vol"] = { "Volga Bulgar", nil, "xbo", } ---------------------------------------------------------------------------------------------------------------------- -- Uralic varieties -- ---------------------------------------------------------------------------------------------------------------------- m["fiu-pro"] = { "Proto-Finno-Ugric", 79890, "urj-pro", } m["urj-fpr-pro"] = { "Proto-Finno-Permic", nil, "urj-pro", } m["krl-nor"] = { "North Karelian", 125501196, "krl", } m["krl-sou"] = { "South Karelian", 129812730, "krl", } m["mns-eas"] = { "Eastern Mansi", 30311755, "mns-cen", } m["mns-wes"] = { "Western Mansi", 30311756, "mns-cen", } ---------------------------------------------------------------------------------------------------------------------- -- Yeneseian varieties -- ---------------------------------------------------------------------------------------------------------------------- m["qfa-yke-pro"] = { "Proto-Ketic", nil, "qfa-yen-pro", family = "qfa-yke", } m["qfa-yko-pro"] = { "Proto-Kottic", nil, "qfa-yen-pro", family = "qfa-yko", } m["qfa-yrn-pro"] = { "Proto-Arinic", nil, "qfa-yen-pro", family = "qfa-yrn", } m["qfa-ypm-pro"] = { "Proto-Pumpokolic", nil, "qfa-yen-pro", family = "qfa-ypm", } ---------------------------------------------------------------------------------------------------------------------- -- Miscellaneous varieties -- ---------------------------------------------------------------------------------------------------------------------- m["mul-tax"] = { "taxonomic name", 522190, "mul", } ----------------------------------------------------- -- Elamite varieties -- ----------------------------------------------------- m["elx-old"] = { "Old Elamite", nil, "elx", } m["elx-mid"] = { "Middle Elamite", nil, "elx", } m["elx-neo"] = { "Neo-Elamite", nil, "elx", } m["elx-ach"] = { "Achaemenid Elamite", nil, "elx", } ----------------------------------------------------- -- Substrates -- ----------------------------------------------------- -- Pre-Roman substrates m["qsb-ibe"] = { "Paleo-Hispanic", 246801, "und", family = "qfa-sub", aliases = {"Palaeo-Hispanic", "Paleohispanic", "Palaeohispanic", "Paleo-Iberian", "Palaeo-Iberian"}, } m["qsb-bal"] = { "Paleo-Balkan", 1815070, "und", family = "qfa-sub", aliases = {"Palaeo-Balkan", "Paleobalkan", "Palaeobalkan"}, } m["xaq"] = { "Aquitanian", 500522, "euq-pro", family = "euq", } return require("Module:languages").finalizeData(m, "language", true) ayfd3iexfsi8lie6hllq8bnqtelg0xf Module:scripts/charToScript 828 7982 54726 2026-09-25T21:04:09Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local subexport = {} local require_when_needed = require("Module:require when needed") local cp = require_when_needed("Module:string utilities", "codepoint") local floor = math.floor local get_plaintext = require_when_needed("Module:utilities", "get_plaintext") local get_script = require_when_needed("Module:scripts", "getByCode") local insert = table.insert local ipairs = ipairs local min = math.min local pairs = pairs local setmetatable = setmetatable local so...' 54726 Scribunto text/plain local subexport = {} local require_when_needed = require("Module:require when needed") local cp = require_when_needed("Module:string utilities", "codepoint") local floor = math.floor local get_plaintext = require_when_needed("Module:utilities", "get_plaintext") local get_script = require_when_needed("Module:scripts", "getByCode") local insert = table.insert local ipairs = ipairs local min = math.min local pairs = pairs local setmetatable = setmetatable local sort = table.sort local split = require_when_needed("Module:string utilities", "split") local table_len = require_when_needed("Module:table", "length") local type = type -- Copied from [[Module:Unicode data]]. local function binaryRangeSearch(codepoint, ranges) local low, mid, high low, high = 1, ranges.length or table_len(ranges) while low <= high do mid = floor((low + high) / 2) local range = ranges[mid] if codepoint < range[1] then high = mid - 1 elseif codepoint <= range[2] then return range, mid else low = mid + 1 end end return nil, mid end -- Copied from [[Module:Unicode data]]. local function linearRangeSearch(codepoint, ranges) for i, range in ipairs(ranges) do if codepoint < range[1] then break elseif codepoint <= range[2] then return range end end end local function compareRanges(range1, range2) return range1[1] < range2[1] end -- Save previously used codepoint ranges in case another character is in the -- same range. local rangesCache = {} --[=[ Takes a codepoint or a character and finds the script code(s) (if any) that are appropriate for it based on the codepoint, using the data module [[Module:scripts/recognition data]]. The data module was generated from the patterns in [[Module:scripts/data]] using [[Module:User:Erutuon/script recognition]]. By default, it returns only the first script code if there are multiple matches (i.e. the code we take to be the default). If `all_scripts` is set, then a table of all matching codes is returned. ]=] local charToScriptData function subexport.charToScript(char, all_scripts) charToScriptData = charToScriptData or mw.loadData("Module:scripts/recognition data") local t = type(char) local codepoint if t == "string" then local etc codepoint, etc = cp(char, 1, 2) if etc then error("bad argument #1 to 'charToScript' (expected a single character)") end elseif t == "number" then codepoint = char else error(("bad argument #1 to 'charToScript' (expected string or a number, got %s)") :format(t)) end local ret = {} local individualMatch = charToScriptData.individual[codepoint] if individualMatch then ret = split(individualMatch, "%s*,%s*", true) else local range if rangesCache[1] then range = linearRangeSearch(codepoint, rangesCache) if range then for i, script in ipairs(range) do if i > 2 then insert(ret, script) if not all_scripts then break end end end end end if not ret[1] then local index = floor(codepoint / 0x1000) range = linearRangeSearch(index, charToScriptData.blocks) if not range and charToScriptData[index] then range = binaryRangeSearch(codepoint, charToScriptData[index]) if range then insert(rangesCache, range) sort(rangesCache, compareRanges) end end if range then for i, script in ipairs(range) do if i > 2 then insert(ret, script) if not all_scripts then break end end end end end end if not ret[1] then insert(ret, "None") end if all_scripts then return ret else return ret[1] end end --[=[ Finds the best script for a string in a language-agnostic way. Converts each character to a codepoint. Iterates the counter for the script code if the codepoint is in the list of individual characters, or if it is in one of the defined ranges in the 4096-character block that it belongs to. Each script has a two-part counter, for primary and secondary matches. Primary matches are when the script is the first one listed; otherwise, it's a secondary match. When comparing scripts, first the total of both are compared (i.e. the overall number of matches). If these are the same, the number of primary and then secondary matches are used as tiebreakers. For example, this is used to ensure that `Grek` takes priority over `Polyt` if no characters which exclusively match `Polyt` are found, as `Grek` is a subset of `Polyt`. If `none_is_last_resort_only` is specified, this will never return None if any characters in `text` belong to a script. Otherwise, it will return None if there are more characters that don't belong to a script than belong to any individual script. (FIXME: This behavior is probably wrong, and `none_is_last_resort_only` should probably become the default.) ]=] function subexport.findBestScriptWithoutLang(text, none_is_last_resort_only) -- `scripts` contains counters for any scripts detected so far. Jpan and Kore are handled as special-cases, as they are combinations of other scripts. local scripts_mt = {Jpan = true, Kore = true} local weights_mt = { __lt = function(a, b) if a[1] + a[2] ~= b[1] + b[2] then return a[1] + a[2] < b[1] + b[2] elseif a[1] ~= b[1] then return a[1] < b[1] elseif a[2] ~= b[2] then return a[2] < b[2] else return false end end } scripts_mt.__index = function(t, k) local ret = {} if k == "Jpan" and scripts_mt.Jpan then for i = 1, 2 do ret[i] = t["Hani"][i] + t["Hira"][i] + t["Kana"][i] end elseif k == "Kore" and scripts_mt.Kore then for i = 1, 2 do ret[i] = t["Hani"][i] + t["Hang"][i] end else for i = 1, 2 do insert(ret, 0) end end return setmetatable(ret, weights_mt) end local scripts = setmetatable({}, scripts_mt) text = get_plaintext(text) local combined_scripts = { Jpan = {["Hani"] = true, ["Hira"] = true, ["Kana"] = true}, Kore = {["Hani"] = true, ["Hang"] = true} } for character in text:gmatch(".[\128-\191]*") do for i, script in ipairs(subexport.charToScript(character, true)) do if not none_is_last_resort_only or script ~= "None" then scripts[script] = scripts[script] local weight = min(i, 2) scripts[script][weight] = scripts[script][weight] + 1 end end end -- Check the combined script counts. If a single constituent has the same count (i.e. it's the only one), discard the combined script. for combined_script, set in pairs(combined_scripts) do for script in pairs(set) do scripts[combined_script] = scripts[combined_script] if (scripts[script][1] + scripts[script][2]) == (scripts[combined_script][1] + scripts[combined_script][2]) then scripts[combined_script] = nil break end end end local bestScript local greatestCount for script, count in pairs(scripts) do if (not greatestCount) or greatestCount < count then bestScript = script greatestCount = count end end bestScript = bestScript or "None" return get_script(bestScript) end return subexport cs19bg8d41yuv7ovit6o3v0kd4ou9kq Module:require when needed 828 7983 54728 2026-09-25T21:04:57Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local getmetatable = getmetatable local ipairs = ipairs local loaded = package.loaded local pairs = pairs local require = require local select = select local setmetatable = setmetatable local tostring = tostring local unpack = unpack or table.unpack -- Lua 5.2 compatibility local function get_nested(obj, ...) local n = select("#", ...) if n == 0 then return obj end obj = obj[...] for i = 2, n do obj = obj[select(i, ...)] end return obj end local func...' 54728 Scribunto text/plain local getmetatable = getmetatable local ipairs = ipairs local loaded = package.loaded local pairs = pairs local require = require local select = select local setmetatable = setmetatable local tostring = tostring local unpack = unpack or table.unpack -- Lua 5.2 compatibility local function get_nested(obj, ...) local n = select("#", ...) if n == 0 then return obj end obj = obj[...] for i = 2, n do obj = obj[select(i, ...)] end return obj end local function get_obj(mt) local obj = require(mt[1]) if #mt > 1 then obj = get_nested(obj, unpack(mt, 2)) end mt[0] = obj return obj end local function __call(self, ...) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end return obj(...) end local function __index(self, k) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end return obj[k] end local function __ipairs(self) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end return ipairs(obj) end local function __newindex(self, k, v) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end obj[k] = v end local function __pairs(self) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end return pairs(obj) end local function __tostring(self) local mt = getmetatable(self) local obj = mt[0] if obj == nil then obj = get_obj(mt) end return tostring(obj) end return function(modname, ...) local mod = loaded[modname] if mod ~= nil then return get_nested(mod, ...) end return setmetatable({}, { modname, __call = __call, __index = __index, __ipairs = __ipairs, __newindex = __newindex, __pairs = __pairs, __tostring = __tostring, -- TODO: other metamethods, if needed. ... }) end 1p8upm5p2cm943t5atixatwvtsenpe2 Module:scripts/recognition data 828 7984 54729 2026-09-25T21:05:39Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local data = { [0x00] = { { 0x00041, 0x0005A, "Latn"}, { 0x00061, 0x0007A, "Latn"}, { 0x000C0, 0x000D6, "Latn"}, { 0x000D8, 0x000F6, "Latn"}, { 0x000F8, 0x0024F, "Latn"}, { 0x00370, 0x003E1, "Grek", "Polyt" }, { 0x003E2, 0x003EF, "Copt" }, { 0x003F0, 0x003FF, "Grek", "Polyt" }, { 0x00400, 0x0045F, "Cyrl" }, { 0x00460, 0x00469, "Cyrs" }, { 0x0046A, 0x0046D, "Cyrl" }, { 0x0046E, 0x00471, "Cyrs" }, { 0x00472, 0x00475, "Cyrl" }, { 0x00476...' 54729 Scribunto text/plain local data = { [0x00] = { { 0x00041, 0x0005A, "Latn"}, { 0x00061, 0x0007A, "Latn"}, { 0x000C0, 0x000D6, "Latn"}, { 0x000D8, 0x000F6, "Latn"}, { 0x000F8, 0x0024F, "Latn"}, { 0x00370, 0x003E1, "Grek", "Polyt" }, { 0x003E2, 0x003EF, "Copt" }, { 0x003F0, 0x003FF, "Grek", "Polyt" }, { 0x00400, 0x0045F, "Cyrl" }, { 0x00460, 0x00469, "Cyrs" }, { 0x0046A, 0x0046D, "Cyrl" }, { 0x0046E, 0x00471, "Cyrs" }, { 0x00472, 0x00475, "Cyrl" }, { 0x00476, 0x00489, "Cyrs" }, { 0x0048A, 0x00527, "Cyrl" }, { 0x00531, 0x0058F, "Armn" }, { 0x00590, 0x005FF, "Hebr" }, { 0x00600, 0x006FF, "Arab" }, { 0x00700, 0x0074F, "Syrc" }, { 0x00750, 0x0077F, "Arab" }, { 0x00780, 0x007B1, "Thaa" }, { 0x007C0, 0x007FF, "Nkoo" }, { 0x00800, 0x0083E, "Samr" }, { 0x00840, 0x0085E, "Mand" }, { 0x00860, 0x0086A, "Syrc" }, { 0x008A0, 0x008FF, "Arab" }, { 0x00900, 0x0097F, "Deva" }, { 0x00980, 0x00983, "Beng" }, { 0x00985, 0x0098C, "Beng" }, { 0x00993, 0x009A8, "Beng" }, { 0x009AA, 0x009B0, "Beng" }, { 0x009B6, 0x009B9, "Beng" }, { 0x009BC, 0x009C4, "Beng" }, { 0x009CB, 0x009CE, "Beng" }, { 0x009E0, 0x009E3, "Beng" }, { 0x009E6, 0x009EF, "Beng" }, { 0x009F0, 0x009F1, "as-Beng" }, { 0x00A01, 0x00A76, "Guru" }, { 0x00A81, 0x00AF1, "Gujr" }, { 0x00B01, 0x00B77, "Orya" }, { 0x00B82, 0x00BFA, "Taml" }, { 0x00C00, 0x00C7F, "Telu" }, { 0x00C80, 0x00CF2, "Knda" }, { 0x00D02, 0x00D7F, "Mlym" }, { 0x00D82, 0x00DF4, "Sinh" }, { 0x00E01, 0x00E5B, "Thai" }, { 0x00E81, 0x00EDF, "Laoo" }, { 0x00F00, 0x00FDA, "Tibt" }, }, [0x01] = { { 0x01000, 0x0109F, "Mymr" }, { 0x010A0, 0x010CD, "Geok" }, { 0x010D0, 0x010FF, "Geor" }, { 0x01100, 0x011FF, "Hang" }, { 0x01200, 0x01399, "Ethi" }, { 0x013A0, 0x013F4, "Cher" }, { 0x01400, 0x0167F, "Cans" }, { 0x01680, 0x0169C, "Ogam" }, { 0x016A0, 0x016F0, "Runr" }, { 0x01700, 0x01714, "Tglg" }, { 0x01720, 0x01734, "Hano" }, { 0x01740, 0x01753, "Buhd" }, { 0x01760, 0x01773, "Tagb" }, { 0x01780, 0x017F9, "Khmr" }, { 0x01800, 0x018AA, "Mong" }, { 0x01900, 0x0194F, "Limb" }, { 0x01950, 0x01974, "Tale" }, { 0x01980, 0x019DF, "Talu" }, { 0x019E0, 0x019FF, "Khmr" }, { 0x01A00, 0x01A1F, "Bugi" }, { 0x01A20, 0x01AAD, "Lana" }, { 0x01B00, 0x01B7C, "Bali" }, { 0x01B80, 0x01BBF, "Sund" }, { 0x01BC0, 0x01BFF, "Batk" }, { 0x01C00, 0x01C4F, "Lepc" }, { 0x01C50, 0x01C7F, "Olck" }, { 0x01C90, 0x01CBF, "Geor" }, { 0x01E00, 0x01EFF, "Latn" }, { 0x01F00, 0x01FFE, "Polyt" }, }, [0x02] = { { 0x02190, 0x021FF, "Zsym" }, { 0x02200, 0x022FF, "Zmth" }, { 0x02300, 0x023FF, "Zsym" }, { 0x02500, 0x027BF, "Zsym" }, { 0x027C0, 0x027EF, "Zmth" }, { 0x02800, 0x028FF, "Brai" }, { 0x02980, 0x02AFF, "Zmth" }, { 0x02B00, 0x02BFE, "Zsym" }, { 0x02C00, 0x02C5E, "Glag" }, { 0x02C60, 0x02C7F, "Latn" }, { 0x02C80, 0x02CFF, "Copt" }, { 0x02D00, 0x02D2D, "Geok" }, { 0x02D30, 0x02D7F, "Tfng" }, { 0x02D80, 0x02DDE, "Ethi" }, { 0x02E80, 0x02FDF, "Hani" }, }, [0x03] = { { 0x03001, 0x03002, "Hani", "Bopo", "Hang", "Hira", "Kana", "Yiii" }, { 0x03003, 0x03007, "Hani" }, { 0x03008, 0x03011, "Hani", "Bopo", "Hang", "Hira", "Kana", "Yiii" }, { 0x03012, 0x03013, "Hani" }, { 0x03014, 0x0301B, "Hani", "Bopo", "Hang", "Hira", "Kana", "Yiii" }, { 0x0301C, 0x0301F, "Hani", "Bopo", "Hang", "Hira", "Kana" }, { 0x03020, 0x0303F, "Hani" }, { 0x03041, 0x0309F, "Hira" }, { 0x030A0, 0x030FF, "Kana" }, { 0x03105, 0x0312F, "Bopo" }, { 0x03131, 0x0318E, "Hang" }, { 0x031A0, 0x031BA, "Bopo" }, { 0x031C0, 0x031E3, "Hani" }, { 0x031F0, 0x031FF, "Kana" }, { 0x03220, 0x03247, "Hani" }, { 0x03280, 0x032B0, "Hani" }, { 0x032C0, 0x032CB, "Hani" }, { 0x03300, 0x03357, "Kana" }, { 0x03358, 0x03370, "Hani" }, { 0x0337B, 0x0337F, "Hani" }, { 0x033E0, 0x033FE, "Hani" }, { 0x03400, 0x03FFF, "Hani" }, }, [0x04] = { { 0x04000, 0x04DB5, "Hani" }, { 0x04E00, 0x04FFF, "Hani" }, }, [0x05] = { { 0x05000, 0x05FFF, "Hani" }, }, [0x06] = { { 0x06000, 0x06FFF, "Hani" }, }, [0x07] = { { 0x07000, 0x07FFF, "Hani" }, }, [0x08] = { { 0x08000, 0x08FFF, "Hani" }, }, [0x09] = { { 0x09000, 0x09FFF, "Hani" }, }, [0x0A] = { { 0x0A000, 0x0A4C6, "Yiii" }, { 0x0A4D0, 0x0A4FF, "Lisu" }, { 0x0A500, 0x0A62B, "Vaii" }, { 0x0A640, 0x0A67F, "Cyrs" }, { 0x0A680, 0x0A697, "Cyrl" }, { 0x0A6A0, 0x0A6F7, "Bamu" }, { 0x0A720, 0x0A7FF, "Latn" }, { 0x0A800, 0x0A82B, "Sylo" }, { 0x0A830, 0x0A832, "Deva", "Dogr", "Gujr", "Guru", "Khoj", "Knda", "Kthi", "Mahj", "Modi", "Nand", "Sind", "Takr", "Tirh"}, { 0x0A833, 0x0A835, "Deva", "Dogr", "Gujr", "Guru", "Khoj", "Knda", "Kthi", "Mahj", "Mlym", "Modi", "Nand", "Sind", "Takr", "Tirh"}, { 0x0A836, 0x0A839, "Deva", "Dogr", "Gujr", "Guru", "Khoj", "Kthi", "Mahj", "Modi", "Sind", "Takr", "Tirh"}, { 0x0A840, 0x0A877, "Phag" }, { 0x0A880, 0x0A8D9, "Saur" }, { 0x0A8E0, 0x0A8FF, "Deva" }, { 0x0A900, 0x0A92F, "Kali" }, { 0x0A930, 0x0A95F, "Rjng" }, { 0x0A980, 0x0A9DF, "Java" }, { 0x0A9E0, 0x0A9FE, "Mymr" }, { 0x0AA00, 0x0AA5F, "Cham" }, { 0x0AA60, 0x0AA7F, "Mymr" }, { 0x0AA80, 0x0AADF, "Tavt" }, { 0x0AAE0, 0x0AAFF, "Mtei" }, { 0x0AB01, 0x0AB2E, "Ethi" }, { 0x0AB30, 0x0AB65, "Latn" }, { 0x0AB70, 0x0ABBF, "Cher" }, { 0x0ABC0, 0x0ABFF, "Mtei" }, { 0x0AC00, 0x0AFFF, "Hang" }, }, [0x0B] = { { 0x0B000, 0x0BFFF, "Hang" }, }, [0x0C] = { { 0x0C000, 0x0CFFF, "Hang" }, }, [0x0D] = { { 0x0D000, 0x0D7A3, "Hang" }, }, [0x0F] = { { 0x0FA27, 0x0FA29, "Hani" }, { 0x0FB13, 0x0FB17, "Armn" }, { 0x0FB1D, 0x0FB4F, "Hebr" }, { 0x0FB50, 0x0FDFD, "Arab" }, { 0x0FE45, 0x0FE46, "Hani", "Bopo", "Hang", "Hira", "Kana" }, { 0x0FE70, 0x0FEFC, "Arab" }, { 0x0FF61, 0x0FF65, "Hani", "Bopo", "Hang", "Hira", "Kana", "Yiii" }, }, [0x10] = { { 0x10000, 0x100FA, "Linb" }, { 0x10280, 0x1029C, "Lyci" }, { 0x102A0, 0x102D0, "Cari" }, { 0x102E1, 0x102FB, "Copt" }, { 0x10300, 0x10323, "Ital" }, { 0x10330, 0x1034A, "Goth" }, { 0x10350, 0x1037A, "Perm" }, { 0x10380, 0x1039F, "Ugar" }, { 0x103A0, 0x103D5, "Xpeo" }, { 0x10400, 0x1044F, "Dsrt" }, { 0x10450, 0x1047F, "Shaw" }, { 0x10480, 0x104A9, "Osma" }, { 0x104B0, 0x104FB, "Osge" }, { 0x10500, 0x10527, "Elba" }, { 0x10530, 0x10563, "Aghb" }, { 0x10600, 0x10767, "Lina" }, { 0x10800, 0x1083F, "Cprt" }, { 0x10840, 0x1085F, "Armi" }, { 0x10860, 0x1087F, "Palm" }, { 0x10880, 0x108AF, "Nbat" }, { 0x108E0, 0x108FF, "Hatr" }, { 0x10900, 0x1091F, "Phnx" }, { 0x10920, 0x1093F, "Lydi" }, { 0x10980, 0x1099F, "Mero" }, { 0x109A0, 0x109BF, "Merc" }, { 0x10A00, 0x10A58, "Khar" }, { 0x10A60, 0x10A7F, "Sarb" }, { 0x10A80, 0x10A9F, "Narb" }, { 0x10AC0, 0x10AF6, "Mani" }, { 0x10B00, 0x10B3F, "Avst" }, { 0x10B40, 0x10B5F, "Prti" }, { 0x10B60, 0x10B7F, "Phli" }, { 0x10B80, 0x10BAF, "Phlp" }, { 0x10C00, 0x10C48, "Orkh" }, { 0x10C80, 0x10CB2, "Hung" }, { 0x10D00, 0x10D39, "Rohg" }, { 0x10E60, 0x10E7E, "Rumin" }, { 0x10F00, 0x10F27, "Sogo" }, { 0x10F30, 0x10F59, "Sogd" }, { 0x10F70, 0x10FAF, "Ougr" }, { 0x10FE0, 0x10FFF, "Elym" }, }, [0x11] = { { 0x11000, 0x1107F, "Brah" }, { 0x11080, 0x110CD, "Kthi" }, { 0x110D0, 0x110F9, "Sora" }, { 0x11100, 0x11146, "Cakm" }, { 0x11150, 0x11176, "Mahj" }, { 0x11180, 0x111D9, "Shrd" }, { 0x11200, 0x1123D, "Khoj" }, { 0x11280, 0x112A9, "Mult" }, { 0x112B0, 0x112F9, "Sind" }, { 0x11301, 0x11374, "Gran" }, { 0x11400, 0x1145E, "Newa" }, { 0x11480, 0x114D9, "Tirh" }, { 0x11580, 0x115DD, "Sidd" }, { 0x11600, 0x11659, "Modi" }, { 0x11680, 0x116C9, "Takr" }, { 0x11700, 0x1173F, "Ahom" }, { 0x11800, 0x1183B, "Dogr" }, { 0x118A0, 0x118FF, "Wara" }, { 0x119A0, 0x119FF, "Nand" }, { 0x11A00, 0x11A47, "Zanb" }, { 0x11A50, 0x11AA2, "Soyo" }, { 0x11AC0, 0x11AF8, "Pauc" }, { 0x11C00, 0x11C6C, "Bhks" }, { 0x11C70, 0x11CB6, "Marc" }, { 0x11D00, 0x11D59, "Gonm" }, { 0x11D60, 0x11DA9, "Gong" }, { 0x11EE0, 0x11EF8, "Maka" }, }, [0x12] = { { 0x12000, 0x1236E, "Xsux" }, { 0x12400, 0x12473, "Xsux" }, { 0x12F90, 0x12FFF, "Cpmn" }, }, [0x13] = { { 0x13000, 0x1342E, "Egyp" }, }, [0x14] = { { 0x14400, 0x14646, "Hluw" }, }, [0x16] = { { 0x16800, 0x16A38, "Bamu" }, { 0x16A40, 0x16A6F, "Mroo" }, { 0x16AD0, 0x16AF5, "Bass" }, { 0x16B00, 0x16B8F, "Hmng" }, { 0x16E40, 0x16E9A, "Medf" }, { 0x16F00, 0x16F9F, "Plrd" }, }, [0x17] = { { 0x17000, 0x17FFF, "Tang" }, }, [0x18] = { { 0x18000, 0x18AF2, "Tang" }, }, [0x1B] = { { 0x1B001, 0x1B11E, "Hira" }, { 0x1B170, 0x1B2FB, "Nshu" }, { 0x1BC00, 0x1BC9F, "Dupl" }, }, [0x1C] = { { 0x1CF00, 0x1CFCF, "Zname" }, }, [0x1D] = { { 0x1D100, 0x1D1DD, "Music" }, { 0x1D2E0, 0x1D2F3, "Maya" }, { 0x1D400, 0x1D7FF, "Zmth" }, { 0x1D800, 0x1DAAF, "Sgnw" }, }, [0x1E] = { { 0x1E000, 0x1E02A, "Glag" }, { 0x1E800, 0x1E8D6, "Mend" }, { 0x1E900, 0x1E95F, "Adlm" }, }, [0x1F] = { { 0x1F000, 0x1F0F5, "Zsym" }, { 0x1F300, 0x1FA6D, "Zsym" }, }, [0x20] = { { 0x20000, 0x20FFF, "Hani" }, }, [0x21] = { { 0x21000, 0x21FFF, "Hani" }, }, [0x22] = { { 0x22000, 0x22FFF, "Hani" }, }, [0x23] = { { 0x23000, 0x23FFF, "Hani" }, }, [0x24] = { { 0x24000, 0x24FFF, "Hani" }, }, [0x25] = { { 0x25000, 0x25FFF, "Hani" }, }, [0x26] = { { 0x26000, 0x26FFF, "Hani" }, }, [0x27] = { { 0x27000, 0x27FFF, "Hani" }, }, [0x28] = { { 0x28000, 0x28FFF, "Hani" }, }, [0x29] = { { 0x29000, 0x29FFF, "Hani" }, }, [0x2A] = { { 0x2A000, 0x2AFFF, "Hani" }, }, [0x2B] = { { 0x2B000, 0x2BFFF, "Hani" }, }, [0x2C] = { { 0x2C000, 0x2CFFF, "Hani" }, }, [0x2D] = { { 0x2D000, 0x2DFFF, "Hani" }, }, [0x2E] = { { 0x2E000, 0x2EBE0, "Hani" }, }, [0x30] = { { 0x30000, 0x30FFF, "Hani" }, }, [0x31] = { { 0x31000, 0x31FFF, "Hani" }, }, [0x32] = { { 0x32000, 0x32FFF, "Hani" }, }, [0x33] = { { 0x33000, 0x33FFF, "Hani" }, }, } for _, v in next, data do v.length = #v end data.individual = { [0x00462] = "Cyrl", [0x00463] = "Cyrl", [0x0060C] = "Arab, Nkoo, Rohg, Syrc, Thaa, Yezi", [0x0061B] = "Arab, Nkoo, Rohg, Syrc, Thaa, Yezi", [0x0061F] = "Arab, Adlm, Nkoo, Rohg, Syrc, Thaa, Yezi", [0x00640] = "Arab, Adlm, Mand, Mani, Ougr, Phlp, Rohg, Sogd, Syrc", [0x00951] = "Deva, Beng, Gran, Gujr, Guru, Knda, Latn, Mlym, Orya, Shrd, Taml, Telu, Tirh", [0x00952] = "Deva, Beng, Gran, Gujr, Guru, Knda, Latn, Mlym, Orya, Taml, Telu, Tirh", [0x00964] = "Deva, Beng, Dogr, Gong, Gonm, Gran, Gujr, Guru, Knda, Mahj, Mlym, Nand, Orya, Sind, Sinh, Sylo, Takr, Taml, Telu, Tirh", [0x00965] = "Deva, Beng, Dogr, Gong, Gonm, Gran, Gujr, Guru, Knda, Limb, Mahj, Mlym, Nand, Orya, Sind, Sinh, Sylo, Takr, Taml, Telu, Tirh", [0x0098F] = "Beng", [0x00990] = "Beng", [0x009A1] = "Beng", [0x009A2] = "Beng", [0x009AF] = "Beng", [0x009B2] = "Beng", [0x009BC] = "Beng", [0x009C7] = "Beng", [0x009C8] = "Beng", [0x009D7] = "Beng", [0x01CDA] = "Deva, Knda, Mlym, Orya, Taml, Telu", [0x01CF2] = "Deva, Beng, Gran, Knda, Nand, Orya, Telu, Tirh", [0x02135] = "Zmth", [0x03000] = "Hani", [0x03003] = "Hani, Bopo, Hang, Hira, Kana", [0x03013] = "Hani, Bopo, Hang, Hira, Kana", [0x03030] = "Hani, Bopo, Hang, Hira, Kana", [0x03037] = "Hani, Bopo, Hang, Hira, Kana", [0x030FB] = "Kana, Hani, Bopo, Hang, Hira, Yiii", [0x032FF] = "Hani", [0x0FA0E] = "Hani", [0x0FA0F] = "Hani", [0x0FA11] = "Hani", [0x0FA13] = "Hani", [0x0FA14] = "Hani", [0x0FA1F] = "Hani", [0x0FA21] = "Hani", [0x0FA23] = "Hani", [0x0FA24] = "Hani", [0x1056F] = "Aghb", [0x16FE0] = "Tang", [0x16FE1] = "Nshu", [0x1B000] = "Kana", } data.blocks = { { 0x04, 0x09, "Hani" }, { 0x0B, 0x0D, "Hang" }, { 0x17, 0x18, "Tang" }, { 0x20, 0x2E, "Hani" }, } return data 4m69qs8gnq8knzros5qbkff3yz21cb5 Module:anchors 828 7985 54731 2026-09-25T21:14:36Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local string_utilities_module = "Module:string utilities" local anchor_encode = mw.uri.anchorEncode local concat = table.concat local insert = table.insert local language_anchor -- Defined below. local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return...' 54731 Scribunto text/plain local export = {} local string_utilities_module = "Module:string utilities" local anchor_encode = mw.uri.anchorEncode local concat = table.concat local insert = table.insert local language_anchor -- Defined below. local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end -- Returns the anchor text to be used as the fragment of a link to a language section. function export.language_anchor(lang, id) return anchor_encode(lang:getFullName() .. ": " .. id) end language_anchor = export.language_anchor -- Normalizes input text (removes formatting etc.), which can then be used as an anchor in an `id=` field. function export.normalize_anchor(str) return decode_entities(anchor_encode(str)) end function export.make_anchors(ids) local anchors = {} for i = 1, #ids do local id = ids[i] local el = mw.html.create("span") :addClass("template-anchor") :attr("id", anchor_encode(id)) :attr("data-id", id) insert(anchors, tostring(el)) end return concat(anchors) end function export.senseid(lang, id, tag_name) -- The following tag is opened but never closed, where is it supposed to be closed? -- with <li> it doesn't matter, as it is closed automatically. -- with <p> it is a problem -- Cannot use mw.html here as it always closes tags return "<" .. tag_name .. " class=\"senseid\" id=\"" .. language_anchor(lang, id) .. "\" data-lang=\"" .. lang:getCode() .. "\" data-id=\"" .. encode_entities(id) .. "\">" end function export.etymid(lang, id) -- Use a <ul> tag to ensure spacing doesn't get messed up. local el = mw.html.create("ul") :addClass("etymid") :attr("id", language_anchor(lang, id)) :attr("data-lang", lang:getCode()) :attr("data-id", id) return tostring(el) end function export.etymonid(lang, id, opts) opts = opts or {} -- Use a <ul> tag to ensure spacing doesn't get messed up. local el = mw.html.create("ul") :addClass("etymonid") :attr("data-lang", lang:getCode()) if id then el:attr("id", language_anchor(lang, id)) el:attr("data-id", id) end if opts.no_tree then el:attr("data-no-tree", "1") end if opts.title then el:attr("data-title", opts.title) end if opts.empty_tree then el:attr("data-empty-tree", "1") end if opts.ety_tree_json then el:attr("data-ety-tree-json", opts.ety_tree_json) end return tostring(el) end return export 2o0jqln8y0bxvpfhe66eogs2k88qkap Module:decorations 828 7986 54732 2026-09-25T21:15:40Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local gender_and_number_module = "Module:gender and number" local labels_module = "Module:labels" local qualifier_module = "Module:qualifier" local references_module = "Module:references" local function track(page) require("Module:debug/track")("decorations/" .. page) return true end --[==[ This function is used by any module that wants to add support for adding decorations (i.e. left and right regular and accent qualifiers, labels and refe...' 54732 Scribunto text/plain local export = {} local gender_and_number_module = "Module:gender and number" local labels_module = "Module:labels" local qualifier_module = "Module:qualifier" local references_module = "Module:references" local function track(page) require("Module:debug/track")("decorations/" .. page) return true end --[==[ This function is used by any module that wants to add support for adding decorations (i.e. left and right regular and accent qualifiers, labels and references, and potentially other types of decorations in the future) to an item, where an "item" is any string of text that might want to be decorated. It is currently used for: # pronunciations and other pronunciation-related items, such as rhymes, hyphenations and homophones, as implemented in [[Module:IPA]], [[Module:rhymes]], [[Module:hyphenation]], [[Module:homophones]] and various language-specific modules such as [[Module:es-pronunc]]; # arbitrary links, as implemented in [[Module:links]]; # headwords, as implemented in [[Module:headword]]; # gender/number specs, as implemented in [[Module:gender and number]]; # *nyms of all sorts (e.g. synonyms, antonyms, hypernyms, hyponyms, coordinate terms, etc.) as implemented in [[Module:nyms]]; # affixes of all sorts, as implemented in [[Module:affix]]; # affix usage examples, as implemented in [[Module:affixusex]]; # column templates as such as {{tl|col}} and {{tl|col3}}, as implemented in [[Module:columns]]; # names (surnames, given names, patronymics, etc.) as implemented in [[Module:names]]; # object-usage qualifiers such as {{tl|+obj}}, as implemented in [[Module:object usage]]; # various inflection templates such as {{tl|ar-conj}} in [[Module:ar-verb]]; # demonyms, as implemented in [[Module:demonym]]; and several other places. Modules that implement templates that occur frequently on a page, such as [[Module:headword]] and [[Module:links]], should consider checking that any decorations exist before loading the module. This is less necessary for other, less-used templates, as this module is not very heavy and does appropriate checks itself to make sure that any decorations are present before taking action. `data` is a structure containing the following fields: * `q`: Optional list of left regular qualifiers, each a string. * `qq`: Optional list of right regular qualifiers, each a string. * `a`: Optional list of left accent qualifiers, each a string. * `aa`: Optional list of right accent qualifiers, each a string. * `l`: Optional list of left labels, each a string. * `ll`: Optional list of right labels, each a string. * `refs`: Optional list of references or reference specs to add directly after the text; the value of a list item is either a string containing the reference text (typically a call to a citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text` (the reference text), `name` (the name of the reference, as in `<nowiki><ref name="foo">...</ref></nowiki>` or `<nowiki><ref name="foo" /></nowiki>`) and/or `group` (the group of the reference, as in `<nowiki><ref name="foo" group="bar">...</ref></nowiki>` or `<nowiki><ref name="foo" group="bar"/></nowiki>`); this uses a parser function to format the reference appropriately and insert a footnote number that hyperlinks to the actual reference, located in the `<nowiki><references /></nowiki>` section. * `genders`: Optional list of gender/number specs as accepted by [[Module:gender and number]]. * `lang`: Language object for accent qualifiers. * `text`: The text to wrap with qualifiers. *` raw`: Don't do any CSS wrapping of the formatted text. The order of qualifiers and labels, on both the left and right, is (1) labels, (2) accent qualifiers, (3) regular qualifiers. This goes in order of relative importance. References and genders go on the right, inside of labels and qualifiers, with references before genders. ]==] function export.format_decorations(data) if not data.text then error("Missing `data.text`; did you try to pass `text` as a separate param?") end if not data.lang then track("nolang") end local text = data.text -- Format the qualifiers and labels that go either before or after the main text. They are ordered as follows, on -- both the left and the right: (1) labels, (2) accent qualifiers, (3) regular qualifiers. This puts the different -- types of qualifiers/labels in order of relative importance. Return nil if no qualifiers or labels, otherwise -- a string containing all formatted qualifiers and labels surrounded by parens. local function format_qualifier_like(labels, accent_qualifiers, qualifiers) local has_qualifiers = qualifiers and qualifiers[1] local has_accent_qualifiers = accent_qualifiers and accent_qualifiers[1] local has_labels = labels and labels[1] if not has_qualifiers and not has_accent_qualifiers and not has_labels then return nil end local qualifier_like_parts = {} local function ins(part) table.insert(qualifier_like_parts, part) end local function format_label_like(labels, mode) return require(labels_module).show_labels { lang = data.lang, labels = labels, nocat = true, mode = mode, open = false, close = false, no_ib_content = true, no_track_already_seen = true, ok_to_destructively_modify = true, -- doesn't apply to `labels` raw = data.raw, } end local m_qualifier = require(qualifier_module) if has_labels then ins(format_label_like(labels)) end if has_accent_qualifiers then ins(format_label_like(accent_qualifiers, "accent")) end if has_qualifiers then ins(m_qualifier.format_qualifiers { qualifiers = qualifiers, open = false, close = false, no_ib_content = true, raw = data.raw, }) end local qualifier_inside local function wrap_qualifier_css(txt, suffix) if data.raw then return txt else return m_qualifier.wrap_qualifier_css(txt, suffix) end end if qualifier_like_parts[2] then qualifier_inside = table.concat(qualifier_like_parts, wrap_qualifier_css(",", "comma") .. " ") else qualifier_inside = qualifier_like_parts[1] end qualifier_like_parts = {} ins(wrap_qualifier_css("(", "brac")) ins(wrap_qualifier_css(qualifier_inside, "content")) ins(wrap_qualifier_css(")", "brac")) return table.concat(qualifier_like_parts) end if data.refs then text = text .. require(references_module).format_references(data.refs) end if data.genders and data.genders[1] then -- NOTE, format_genders() returns a second value (categories) but we ignore it. text = text .. "&nbsp;" .. require(gender_and_number_module).format_genders(data.genders, data.lang) end if data.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("`.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end local leftq = format_qualifier_like(data.l, data.a, data.q) local rightq = format_qualifier_like(data.ll, data.aa, data.qq) if leftq then text = leftq .. " " .. text end if rightq then text = text .. " " .. rightq end return text end function export.format_qualifiers(...) -- FIXME: Added 2026-09-17. Remove after a month or less. error("use format_decorations instead") end return export gvyopr8wgnkfw3arhfcjf23ibitjoha Module:form of 828 7987 54733 2026-09-25T21:16:34Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} export.force_cat = false -- for testing; set to true to display categories even on non-mainspace pages local debug_track_module = "Module:debug/track" local etymology_module = "Module:etymology" local form_of_cats_module = "Module:form of/cats" local form_of_data_module = "Module:form of/data" local form_of_data1_module = "Module:form of/data/1" local form_of_data2_module = "Module:form of/data/2" local form_of_functions_module = "Module:form...' 54733 Scribunto text/plain local export = {} export.force_cat = false -- for testing; set to true to display categories even on non-mainspace pages local debug_track_module = "Module:debug/track" local etymology_module = "Module:etymology" local form_of_cats_module = "Module:form of/cats" local form_of_data_module = "Module:form of/data" local form_of_data1_module = "Module:form of/data/1" local form_of_data2_module = "Module:form of/data/2" local form_of_functions_module = "Module:form of/functions" local form_of_lang_data_module_prefix = "Module:form of/lang-data/" local function_module = "Module:fun" local headword_data_module = "Module:headword/data" local json_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local load_module = "Module:load" local parse_utilities_module = "Module:parse utilities" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local table_deep_equals_module = "Module:table/deepEquals" local utilities_module = "Module:utilities" local anchor_encode = mw.uri.anchorEncode local concat = table.concat local dump = mw.dumpObject local fetch_categories_and_labels -- Defined below. local format_form_of -- Defined below. local get_tag_display_form -- Defined below. local get_tag_set_display_form -- Defined below. local insert = table.insert local ipairs = ipairs local is_link_or_html -- Defined below. local list_to_text = mw.text.listToText local lookup_shortcut -- Defined below. local lookup_tag -- Defined below. local normalize_tag_set -- Defined below. local parse_tag_set_properties -- Defined below. local require = require local sort = table.sort local split_tag_set -- Defined below. local type = type --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function append(...) append = require(table_module).append return append(...) end local function contains(...) contains = require(table_module).contains return contains(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function deep_equals(...) deep_equals = require(table_deep_equals_module) return deep_equals(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function full_link(...) full_link = require(links_module).full_link return full_link(...) end local function insert_if_not(...) insert_if_not = require(table_module).insertIfNot return insert_if_not(...) end local function is_subset_list(...) is_subset_list = require(table_module).isSubsetList return is_subset_list(...) end local function iterate_from(...) iterate_from = require(function_module).iterateFrom return iterate_from(...) end local function join_multiparts(...) join_multiparts = require(form_of_functions_module).join_multiparts return join_multiparts(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function parse_inline_modifiers(...) parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers return parse_inline_modifiers(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function safe_load_data(...) safe_load_data = require(load_module).safe_load_data return safe_load_data(...) end local function safe_require(...) safe_require = require(load_module).safe_require return safe_require(...) end local function serial_comma_join(...) serial_comma_join = require(table_module).serialCommaJoin return serial_comma_join(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function show_labels(...) show_labels = require(labels_module).show_labels return show_labels(...) end local function slice(...) slice = require(table_module).slice return slice(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function ucfirst(...) ucfirst = require(string_utilities_module).ucfirst return ucfirst(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local cat_functions local function get_cat_functions() cat_functions, get_cat_functions = require(form_of_functions_module).cat_functions, nil return cat_functions end local default_pagename local function get_default_pagename() default_pagename, get_default_pagename = load_data(headword_data_module).pagename, nil return default_pagename end local display_handlers local function get_display_handlers() display_handlers, get_display_handlers = require(form_of_functions_module).display_handlers, nil return display_handlers end local m_cats_data local function get_m_cats_data() m_cats_data, get_m_cats_data = load_data(form_of_cats_module), nil return m_cats_data end local m_data local function get_m_data() -- Needs require. m_data, get_m_data = require(form_of_data_module), nil return m_data end local m_data1 local function get_m_data1() m_data1, get_m_data1 = load_data(form_of_data1_module), nil return m_data1 end local m_data2 local function get_m_data2() m_data2, get_m_data2 = load_data(form_of_data2_module), nil return m_data2 end local m_pos_data local function get_m_pos_data() m_pos_data, get_m_pos_data = load_data(headword_data_module).pos_aliases, nil return m_pos_data end --[==[ intro: This module implements the underlying processing of {{tl|form of}}, {{tl|inflection of}} and specific variants such as {{tl|past participle of}} and {{tl|alternative spelling of}}. Most of the logic in this file is to handle tags in {{tl|inflection of}}. Other related files: * [[Module:form of/templates]] contains the majority of the logic that implements the templates themselves. * [[Module:form of/data/1]] is a data-only file containing information on the more common inflection tags, listing the tags, their shortcuts, the category they belong to (tense-aspect, case, gender, voice-valence, etc.), the appropriate glossary link and the wikidata ID. * [[Module:form of/data/2]] is a data-only file containing information on the less common inflection tags, in the same format as [[Module:form of/data/1]]. * [[Module:form of/lang-data/LANGCODE]] is a data-only file containing information on the language-specific inflection tags for the language with code LANGCODE, in the same format as [[Module:form of/data/1]]. Language-specific tags override general tags. * [[Module:form of/cats]] is a data-only file listing the language-specific categories that are added when the appropriate combinations of tags are seen for a given language. * [[Module:form of/functions]] contains functions for use with [[Module:form of/data/1]] and [[Module:form of/cats]]. They are contained in this module because data-only modules can't contain code. The functions in this file are of two types: *# Display handlers allow for customization of the display of multipart tags (see below). Currently there is only one such handler, for handling multipart person tags such as `1//2//3`. *# Cat functions allow for more complex categorization logic, and are referred to by name in [[Module:form of/cats]]. Currently no such functions exist. The following terminology is used in conjunction with {{tl|inflection of}}: * A ''tag'' is a single grammatical item, as specified in a single numbered parameter of {{tl|inflection of}}. Examples are `masculine`, `nominative`, or `first-person`. Tags may be abbreviated, e.g. `m` for `masculine`, `nom` for `nominative`, or `1` for `first-person`. Such abbreviations are called ''aliases'', and some tags have multiple equivalent aliases (e.g. `p` or `pl` for `plural`). The full, non-abbreviated form of a tag is called its ''canonical form''. * The ''display form'' of a tag is the way it's displayed to the user. Usually the displayed text of the tag is the same as its canonical form, and it normally functions as a link to a glossary entry explaining the tag. Usually the link is to an entry in [[Appendix:Glossary]], but sometimes the tag is linked to an individual dictionary entry or to a Wikipedia entry. Occasionally, the display text differs from the canonical form of the tag. An example is the tag `comparative case`, which has the display text read as simply `comparative`. Normally, tags referring to cases don't have the word "case" in them, but in this case the tag `comparative` was already used as an alias for the tag `comparative degree`, so the tag was named `comparative case` to avoid clashing. A similar situation occurs with `adverbial case` vs. the grammar tag `adverbial` (as in `adverbial participle`). * A ''tag set'' is an ordered list of tags, which together express a single inflection, for example, `1|s|pres|ind`, which can be expanded to canonical-form tags as `first-person|singular|present|indicative`. * A ''conjoined tag set'' is a tag set that consists of multiple individual tag sets separated by a semicolon, e.g. `1|s|pres|ind|;|2|s|imp`, which specifies two tag sets, `1|s|pres|ind` as above and `2|s|imp` (in canonical form, `second-person|singular|imperative`). Multiple tag sets specified in a single call to {{tl|inflection of}} are specified in this fashion. Conjoined tag sets can also occur in list-tag shortcuts. * A ''multipart tag'' is a tag that embeds multiple tags within it, such as `f//n` or `nom//acc//voc`. These are used in the case of [[syncretism]], when the same form applies to multiple inflections. Examples are the Spanish present subjunctive, where the first-person and third-person singular have the same form (e.g. {{m|es|siga}} from {{m|es|seguir|t=to follow}}), or Latin third-declension adjectives, where the dative and ablative plural of all genders have the same form (e.g. {{m|la|omnibus}} from {{m|la|omnis|t=all}}). These would be expressed respectively as `1//3|s|pres|sub` and `dat//abl|m//f//n|p`, where the use of the multipart tag compactly encodes the syncretism and avoids the need to individually list out all of the inflections. Multipart tags currently display as a list separated by a slash, e.g. ''dative/ablative'' or ''masculine/feminine/neuter'' where each individual word is linked appropriately. As a special case, multipart tags involving persons display specially; for example, the multipart tag `1//2//3` displays as ''first-, second- and third-person'', with the word "person" occurring only once. * A ''two-level multipart tag'' is a special type of multipart tag that joins two or more tag sets instead of joining individual tags. The tags within the tag set are joined by a colon, e.g. `1:s//3:p`, which is displayed as ''first-person singular and third-person plural'', e.g. for use with the form {{m|grc|μέλλον}} of the verb {{m|grc|μέλλω|t=to intend}}, which uses the tag set `1:s//3:p|impf|actv|indc|unaugmented` to express the syncretism between the first singular and third plural forms of the imperfect active indicative unaugmented conjugation. Two-level multipart tags should be used sparingly; if in doubt, list out the inflections separately. [FIXME: Make two-level multipart tags obsolete.] * A ''shortcut'' is a tag that expands to any type of tag described above, or to any type of tag set described above. Aliases are a particular type of shortcut whose expansion is a single non-multipart tag. * A ''multipart shortcut'' is a shortcut that expands into a multipart tag, for example `123`, which expands to the multipart tag `1//2//3`. Only the most common such combinations exist as shortcuts. * A ''list shortcut'' is a special type of shortcut that expands to a list of tags instead of a single tag. For example, the shortcut `1s` expands to `1|s` (first-person singular). Only the most common such combinations exist as shortcuts. * A ''conjoined shortcut'' is a special type of list shortcut that consists of a conjoined tag set (multiple logical tag sets). For example, the English language-specific shortcut `ed-form` expands to `spast|;|past|part`, expressing the common syncretism between simple past and past participle in English (and in this case, `spast` is itself a list shortcut that expands to `simple|past`).]==] -- Add tracking category for PAGE when called from {{inflection of}} or -- similar TEMPLATE. The tracking category linked to is -- [[Wiktionary:Tracking/inflection of/PAGE]]. local function track(page) debug_track("inflection of/" .. -- avoid including links in pages (may cause error) page:gsub("%[", "("):gsub("%]", ")"):gsub("|", "!") ) end local function wrap_in_span(text, classes) if classes then return ("<span class='%s'>%s</span>"):format(classes, text) else return text end end local function show_linked_term(data) local termobj, face, span_classes, ok_to_destructively_modify, overall_lang, text_classes = data.termobj, data.face, data.span_classes, data.ok_to_destructively_modify, data.overall_lang, data.text_classes local need_to_copy, pretext_lang local categories = {} if overall_lang and overall_lang:getCode() ~= termobj.lang:getCode() then local lang_display lang_display, categories = require(etymology_module).insert_source_cat_get_display { lang = data.overall_lang, source = termobj.lang, } pretext_lang = wrap_in_span(lang_display .. " ", text_classes) end local has_decoration = termobj.q or termobj.qq or termobj.a or termobj.aa or termobj.l or termobj.ll or termobj.refs -- `show_decorations` is set by parse_{term,list}_with_inline_modifiers_and_separate_params in -- [[Module:parameter utilities]], called in [[Module:form of/templates]], so in most circumstances -- it's already set and we don't need to do a shallow-copy to set it. local need_to_set_show_decorations = has_decoration and not termobj.show_decorations need_to_copy = not ok_to_destructively_modify and (pretext_lang or need_to_set_show_decorations) if need_to_copy then termobj = shallow_copy(termobj) end if pretext_lang then termobj.pretext = pretext_lang end if need_to_set_show_decorations then termobj.show_decorations = true end return wrap_in_span(full_link(termobj, face), span_classes), categories end --[==[ Lowest-level implementation of form-of templates, including the general {{tl|form of}} as well as those that deal with inflection tags, such as the general {{tl|inflection of}}, semi-specific variants such as {{tl|participle of}}, and specific variants such as {{tl|past participle of}}. `data` contains all the information controlling the display, with the following fields: * `.lang`: Overall language of the form-of template. If specified, any lemmas, enclitics or base lemmas that are of a different language will have that language displayed before the term in question. * `.text`: Text to insert before the lemmas. Wrapped in the value of `.text_classes`, or its default; see below. * `.lemmas`: List of objects describing the lemma(s) of which the term in question is a non-lemma form. These are passed directly to {full_link()} in [[Module:links]]. Each object should have at minimum a `.lang` field containing the language of the lemma and a `.term` field containing the lemma itself. Each object is formatted using {full_link()} and then if there are more than one, they are joined using {serialCommaJoin()} in [[Module:table]]. Alternatively, `.lemmas` can be a string, which is displayed directly, or omitted, to show no lemma links and omit the connecting text. * `.lemma_face`: "Face" to use when displaying the lemma objects. Usually should be set to {"term"}. * `.conj`: Conjunction or separator to use when joining multiple lemma objects. If {nil}, defaults to {"and"}. If this has the value {false}, the lemmas are preceded with the `.separator` field in each lemma. * `.enclitics`: List of enclitics to display after the lemmas, in parens. * `.enclitic_conj`: Conjunction or separator to use when joining multiple enclitics. Defaults to {"and"}. * `.base_lemmas`: List of base lemmas to display after the lemmas, in the case where the lemmas in `.lemmas` are themselves forms of another lemma (the base lemma), e.g. a comparative, superlative or participle. Each object is of the form { { paramobj = PARAM_OBJ, lemmas = {LEMMA_OBJ, LEMMA_OBJ, ...}, conj = "CONJ" }} where PARAM_OBJ describes the properties of the base lemma parameter (i.e. the relationship between the intermediate and base lemmas); LEMMA_OBJ is an object suitable to be passed to {full_link()} in [[Module:links]]; and CONJ is the conjunction to join multiple lemmas with, defaulting to {"and"}. PARAM_OBJ is of the format { { param = "PARAM", tags = {"TAG", "TAG", ...} } where PARAM is the name of the parameter to {{tl|inflection of}} etc. that holds the base lemma(s) of the specified relationship and the tags describe the relationship, such as { {"comd"}} or { {"past", "part"}}. * `.text_classes`: CSS classes used to wrap the tag text and lemma links. Default is {"form-of-definition use-with-mention"}. Use `false` for no wrapping. * `.lemma_classes`: Additional CSS classes used to wrap the lemma links. Default is {"form-of-definition-link"}. Use `false` for no wrapping. * `.posttext`: Additional text to display after the lemma links. * `.ok_to_destructively_modify`: If set, data structures (including the nested lemma structures) can be modified in-place to save memory; otherwise they will be copied before modifying. Returns two values, the formatted string and any categories to add the page to (which will arise if `.lang` is specified and a language other than `.lang` is given in one of the lemmas in `.lemmas` or enclitics in `.enclitics`). ]==] function export.format_form_of(data) if type(data) ~= "table" then error("Internal error: First argument must now be a table of arguments") end local text_classes = data.text_classes if text_classes == nil and not data.nowrap then text_classes = "form-of-definition use-with-mention" end local lemma_classes = data.lemma_classes if lemma_classes == nil then lemma_classes = "form-of-definition-link" end local parts = {} if text_classes then insert(parts, "<span class='" .. text_classes .. "'>") end insert(parts, data.text) if data.text ~= "" and data.lemmas then insert(parts, " ") end local categories = {} if data.lemmas then if type(data.lemmas) == "string" then insert(parts, wrap_in_span(data.lemmas, lemma_classes)) else local formatted_terms = {} for _, lemma in ipairs(data.lemmas) do local linked_term, this_categories = show_linked_term { termobj = lemma, face = data.lemma_face, span_classes = lemma_classes, ok_to_destructively_modify = data.ok_to_destructively_modify, overall_lang = data.lang, text_classes = text_classes } if this_categories[1] then extend(categories, this_categories) end if data.conj == false and lemma.separator then insert(formatted_terms, lemma.separator) end insert(formatted_terms, linked_term) end if data.conj == false then insert(parts, concat(formatted_terms)) else insert(parts, serial_comma_join(formatted_terms, {conj = data.conj or "and"})) end end if data.lit then insert(parts, ", literally " .. require(links_module).mark(data.lit, "gloss")) end end if data.enclitics and #data.enclitics > 0 then -- The outer parens need to be outside of the text_classes span so they show in upright instead of italic, or -- they will clash with upright parens generated by link annotations such as transliterations and pos=. if text_classes then insert(parts, "</span>") end local formatted_terms = {} for _, enclitic in ipairs(data.enclitics) do -- FIXME, should we have separate clitic face and/or classes? local linked_term, this_categories = show_linked_term { termobj = enclitic, face = data.lemma_face, span_classes = lemma_classes, ok_to_destructively_modify = data.ok_to_destructively_modify, overall_lang = data.lang, text_classes = text_classes } if this_categories[1] then extend(categories, this_categories) end insert(formatted_terms, linked_term) end insert(parts, " (") insert(parts, wrap_in_span("with enclitic" .. (#data.enclitics > 1 and "s" or "") .. " ", text_classes)) insert(parts, serial_comma_join(formatted_terms, {conj = data.enclitic_conj or "and"})) insert(parts, ")") if text_classes then insert(parts, "<span class='" .. text_classes .. "'>") end end if data.base_lemmas and #data.base_lemmas > 0 then for _, base_lemma in ipairs(data.base_lemmas) do insert(parts, ", the ") if text_classes then insert(parts, "</span>") end insert(parts, (export.tagged_inflections { lang = data.lang or base_lemma.lemmas[1].lang, tags = base_lemma.paramobj.tags, lemmas = base_lemma.lemmas, conj = base_lemma.conj or "and", lemma_face = data.lemma_face, no_format_categories = true, nocat = true, text_classes = data.text_classes, ok_to_destructively_modify = data.ok_to_destructively_modify, })) if text_classes then insert(parts, "<span class='" .. text_classes .. "'>") end end end -- FIXME, should posttext go before enclitics? If so we need to have separate handling for the -- final colon when there are multiple tag sets in tagged_inflections(). if data.posttext then insert(parts, data.posttext) end if text_classes then insert(parts, "</span>") end return concat(parts), categories end format_form_of = export.format_form_of --[==[ Return true if `tag` contains an internal link or HTML.]==] function export.is_link_or_html(tag) return tag:find("[[", nil, true) or tag:find("|", nil, true) or tag:find("<", nil, true) end is_link_or_html = export.is_link_or_html --[==[ Look up a tag (either a shortcut of any sort of a canonical long-form tag) and return its expansion. The expansion will be a string unless the shortcut is a list-tag shortcut such as `1s`; in that case, the expansion will be a list. The caller must handle both cases. Only one level of expansion happens; hence, `acc` expands to {"accusative"}, `1s` expands to { {"1", "s"}} (not to { {"first", "singular"}}) and `123` expands to {"1//2//3"}. The expansion will be the same as the passed-in tag in the following circumstances: # The tag is `;` (this is special-cased, and no lookup is done). # The tag is a multipart tag such as `nom//acc` (this is special-cased, and no lookup is done). # The tag contains a raw link (this is special-cased, and no lookup is done). # The tag contains HTML (this is special-cased, and no lookup is done). # The tag is already a canonical long-form tag. # The tag is unrecognized. This function first looks up in the lang-specific data module [[Module:form of/lang-data/LANGCODE]], then in [[Module:form of/data/1]] (which includes more common non-lang-specific tags) and finally (only if the tag is not recognized as a shortcut or canonical tag, and is not of types 1-4 above) in [[Module:form of/data/2]]. If the expansion is a string and is different from the tag, track it if `do_track` is true.]==] function export.lookup_shortcut(tag, lang, do_track) -- If there is HTML or a link in the tag, return it directly; don't try -- to look it up, which will fail. if tag == ";" or tag:find("//", nil, true) or is_link_or_html(tag) then return tag end local expansion while lang do local langdata = safe_load_data(form_of_lang_data_module_prefix .. lang:getCode()) -- If this is a canonical long-form tag, just return it, and don't check for shortcuts. This is an -- optimization; see below. if langdata then if langdata.tags[tag] then return tag end expansion = langdata.shortcuts[tag] if expansion then break end end -- If the language has a parent (i.e. a superordinate variety), try again with that. lang = lang:getParent() end if not expansion then -- If this is a canonical long-form tag, just return it, and don't check for shortcuts (which will cause -- [[Module:form of/data/2]] to be loaded, because there won't be a shortcut entry in [[Module:form of/data/1]] -- -- or, for that matter, in [[Module:form of/data/2]]). This is an optimization; the code will still work without -- it, but will use up more memory. if (m_data1 or get_m_data1()).tags[tag] then return tag end expansion = m_data1.shortcuts[tag] end if not expansion then expansion = (m_data2 or get_m_data2()).shortcuts[tag] end if not expansion then return tag end -- Maybe track the expansion if it's not the same as the raw tag. if do_track and expansion ~= tag and type(expansion) == "string" then track("tag/" .. tag) end return expansion end lookup_shortcut = export.lookup_shortcut --[==[ Look up a normalized/canonicalized tag and return the data object associated with it. If the tag isn't found, return nil. This first looks up in the lang-specific data module [[Module:form of/lang-data/LANGCODE]], then in [[Module:form of/data/1]] (which includes more common non-lang-specific tags) and then finally in [[Module:form of/data/2]].]==] function export.lookup_tag(tag, lang) while lang do local langdata = safe_load_data(form_of_lang_data_module_prefix .. lang:getCode()) local tag = langdata and langdata.tags[tag] if tag then return tag end -- If the language has a parent (i.e. a superordinate variety), try again with that. lang = lang:getParent() end local tagobj = (m_data1 or get_m_data1()).tags[tag] if tagobj then return tagobj end local tagobj2 = (m_data2 or get_m_data2()).tags[tag] if tagobj2 then return tagobj2 end return nil end lookup_tag = export.lookup_tag -- Normalize a single tag, which may be a shortcut but should not be a multipart tag, a multipart shortcut or a list -- shortcut. local function normalize_single_tag(tag, lang, do_track) local expansion = lookup_shortcut(tag, lang, do_track) if type(expansion) ~= "string" then error("Tag '" .. tag .. "' is a list shortcut, which is not allowed here") end tag = expansion if not lookup_tag(tag, lang) and do_track then -- If after all expansions and normalizations we don't recognize the canonical tag, track it. track("unknown") track("unknown/" .. tag) end return tag end --[=[ Normalize a component of a multipart tag. This should not have any // in it, but may join multiple individual tags with a colon, and may be a single list-tag shortcut, which is treated as if colon-separated. The return value may be a list of tags. ]=] local function normalize_multipart_component(tag, lang, do_track) -- If there is HTML or a link in the tag, don't try to split on colon. A colon may legitimately occur in either one, -- and we don't want these things parsed. Note that we don't do this check before splitting on //, which we don't -- expect to occur in links or HTML; see comment in normalize_tag(). if is_link_or_html(tag) then return tag end local components = split(tag, ":", true) if #components == 1 then -- We allow list-tag shortcuts inside of multipart tags, e.g. -- '1s//3p'. Check for this now. tag = lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then -- Temporary tracking as we will disallow this. track("list-tag-inside-of-multipart") -- We found a list-tag shortcut; treat as if colon-separated. components = tag else return normalize_single_tag(tag, lang, do_track) end end local normtags = {} -- Temporary tracking as we will disallow this. track("two-level-multipart") for _, component in ipairs(components) do if do_track then -- There are multiple components; track each of the individual -- raw tags. track("tag/" .. component) end insert(normtags, normalize_single_tag(component, lang, do_track)) end return normtags end --[=[ Normalize a single tag. The return value may be a list (in the case of multipart tags), which will contain nested lists in the case of two-level multipart tags. ]=] local function normalize_tag(tag, lang, do_track) -- We don't check for links or HTML before splitting on //, which we don't expect to occur in links or HTML. Doing -- it this way allows for a tag like '{{lb|grc|Epic}}//{{lb|grc|Ionic}}' to function correctly (the template calls -- will be expanded before we process the tag, and will contain links and HTML). The only check we do is for a URL, -- which shouldn't normally occur, but might if the user tries to put an external link into the tag. URL's with // -- normally have the sequence ://, which should never normally occur when // and : are used in their normal ways. if tag:find("://", nil, true) then return tag end local split_tags = split(tag, "//", true) if #split_tags == 1 then local retval = normalize_multipart_component(tag, lang, do_track) if type(retval) == "table" then -- The user gave a tag like '1:s', i.e. with colon but without //. Allow this, but we need to return a -- nested list. return {retval} end return retval end local normtags = {} for _, single_tag in ipairs(split_tags) do if do_track then -- If the tag was a multipart tag, track each of individual raw tags. track("tag/" .. single_tag) end insert(normtags, normalize_multipart_component(single_tag, lang, do_track)) end return normtags end --[==[ Normalize a tag set (a list of tags) into its canonical-form tags. The return value is a list of normalized tag sets (a list because of there may be conjoined shortcuts among the input tags). A normalized tag set is a list of tag elements, where each element is either a string (the canonical form of a tag), a list of such strings (in the case of multipart tags) or a list of lists of such strings (in the case of two-level multipart tags). For example, the multipart tag `nom//acc//voc` will be represented in canonical form as { {"nominative", "accusative", "vocative"}}, and the two-level multipart tag `1:s//3:p` will be represented as { {{"first-person", "singular"}, {"third-person", "plural"}}}. Example 1: {normalize_tag_set({"nom//acc//voc", "n", "p"})} = { {{{"nominative", "accusative", "vocative"}, "masculine", "plural"}}} Example 2: {normalize_tag_set({"ed-form"}, ENGLISH)} = { {{"simple", "past"}, {"past", "participle"}}} Example 3: {normalize_tag_set({"archaic", "ed-form"}, ENGLISH)} = { {{"archaic", "simple", "past"}, {"archaic", "past", "participle"}}}]==] function export.normalize_tag_set(tag_set, lang, do_track) -- We track usage of shortcuts, normalized forms and (in the case of multipart tags or list tags) intermediate -- forms. For example, if the tags 1s|mn|gen|indefinite are passed in, we track the following: -- [[Wiktionary:Tracking/inflection of/tag/1s]] -- [[Wiktionary:Tracking/inflection of/tag/1]] -- [[Wiktionary:Tracking/inflection of/tag/s]] -- [[Wiktionary:Tracking/inflection of/tag/first-person]] -- [[Wiktionary:Tracking/inflection of/tag/singular]] -- [[Wiktionary:Tracking/inflection of/tag/mn]] -- [[Wiktionary:Tracking/inflection of/tag/m//n]] -- [[Wiktionary:Tracking/inflection of/tag/m]] -- [[Wiktionary:Tracking/inflection of/tag/n]] -- [[Wiktionary:Tracking/inflection of/tag/masculine]] -- [[Wiktionary:Tracking/inflection of/tag/neuter]] -- [[Wiktionary:Tracking/inflection of/tag/gen]] -- [[Wiktionary:Tracking/inflection of/tag/genitive]] -- [[Wiktionary:Tracking/inflection of/tag/indefinite]] local output_tag_set = {} local saw_semicolon = false for _, tag in ipairs(tag_set) do if do_track then -- Track the raw tag. track("tag/" .. tag) end -- Expand the tag, which may generate a new tag (either a fully canonicalized tag, a multipart tag, or a list -- of tags). tag = lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then if contains(tag, ";") then -- If we saw a conjoined shortcut, we need to use a more general algorithm that can expand a single -- tag set into multiple. saw_semicolon = true break end for _, t in ipairs(tag) do if do_track then -- If the tag expands to a list of raw tags, track each of those. track("tag/" .. t) end insert(output_tag_set, normalize_tag(t, lang, do_track)) end else insert(output_tag_set, normalize_tag(tag, lang, do_track)) end end if not saw_semicolon then return {output_tag_set} end -- Use a more general algorithm that handles conjoined shortcuts. output_tag_set = {} for i, tag in ipairs(tag_set) do if do_track then -- Track the raw tag. track("tag/" .. tag) end -- Expand the tag, which may generate a new tag (either a fully canonicalized tag, a multipart tag, or a list -- of tags). tag = lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then local output_tag_sets = {} local shortcut_tag_sets = split_tag_set(tag) local normalized_shortcut_tag_sets = {} for _, shortcut_tag_set in ipairs(shortcut_tag_sets) do extend(normalized_shortcut_tag_sets, normalize_tag_set(shortcut_tag_set, lang, do_track)) end local after_tags = slice(tag_set, i + 1) local normalized_after_tags_sets = normalize_tag_set(after_tags, lang, do_track) for _, normalized_shortcut_tag_set in ipairs(normalized_shortcut_tag_sets) do for _, normalized_after_tags_set in ipairs(normalized_after_tags_sets) do insert(output_tag_sets, append(output_tag_set, normalized_shortcut_tag_set, normalized_after_tags_set)) end end return output_tag_sets else insert(output_tag_set, normalize_tag(tag, lang, do_track)) end end error("Internal error: Should not get here") end normalize_tag_set = export.normalize_tag_set --[==[ Split a tag set that may consist of multiple semicolon-separated tag sets into the component tag sets.]==] function export.split_tag_set(tag_set) local split_tag_sets = {} local cur_tag_set = {} for _, tag in ipairs(tag_set) do if tag == ";" then if #cur_tag_set > 0 then insert(split_tag_sets, cur_tag_set) end cur_tag_set = {} else insert(cur_tag_set, tag) end end if #cur_tag_set > 0 then insert(split_tag_sets, cur_tag_set) end return split_tag_sets end split_tag_set = export.split_tag_set local tag_set_param_mods = { lb = { item_dest = "labels", convert = function(arg, _parse_err) return split(arg, "//", true) end, } } --[==[ Parse tag set properties from a tag set (list of tags). Currently no per-tag properties are recognized, and the only per-tag-set property recognized is `<lb:...>` for specifing label(s) for the tag set. Per-tag-set properties must be attached to the last tag.]==] function export.parse_tag_set_properties(tag_set) local function generate_tag_set_obj(last_tag) tag_set[#tag_set] = last_tag return {tags = tag_set} end local last_tag = tag_set[#tag_set] -- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude HTML entry with <span ...>, <i ...>, <br/> or -- similar in it, caused by wrapping an argument in {{l|...}}, {{af|...}} or similar. Basically, all tags of -- the sort we parse here should consist of a less-than sign, plus letters, plus a colon, e.g. <lb:...>, so if -- we see a tag on the outer level that isn't in this format, we don't try to parse it. The restriction to the -- outer level is to allow generated HTML inside of e.g. qualifier tags, such as foo<q:similar to {{m|fr|bar}}>. if last_tag:find("<", nil, true) and not last_tag:find("^[^<]*<%l*[^%l:]") then return parse_inline_modifiers(last_tag, { param_mods = tag_set_param_mods, generate_obj = generate_tag_set_obj, }) else return generate_tag_set_obj(last_tag) end end parse_tag_set_properties = export.parse_tag_set_properties local function normalize_pos(pos) if not pos then return nil end return (m_pos_data or get_m_pos_data())[pos] or pos end -- Return the display form of a single canonical-form tag. The value -- passed in must be a string (i.e. it cannot be a list describing a -- multipart tag). To handle multipart tags, use get_tag_display_form(). -- A truthy `nolink` suppresses linking. local function get_single_tag_display_form(normtag, lang, nolink) local data = lookup_tag(normtag, lang) local display = normtag -- If the tag has a special display form, use it if data and data.display then display = data.display if nolink then display = remove_links(display) end end -- If there is a nonempty glossary index, then show a link to it if not nolink then local glossary = data and data[(m_data or get_m_data()).GLOSSARY] if glossary ~= nil then if glossary == m_data.WIKT then display = "[[" .. normtag .. "|" .. display .. "]]" elseif glossary == m_data.WP then display = "[[w:" .. normtag .. "|" .. display .. "]]" elseif glossary == m_data.APPENDIX then display = "[[Appendix:Glossary#" .. anchor_encode(normtag) .. "|" .. display .. "]]" elseif type(glossary) ~= "string" then error(("Internal error: Wrong type %s for glossary value %s for tag %s"):format( type(glossary), dump(glossary), normtag)) else local link = glossary:match("^wikt:(.*)") if link then display = "[[" .. link .. "|" .. display .. "]]" end if not link then link = glossary:match("^w:(.*)") if link then display = "[[w:" .. link .. "|" .. display .. "]]" end end if not link then display = "[[Appendix:Glossary#" .. anchor_encode(glossary) .. "|" .. display .. "]]" end end end end return display end --[==[ Turn a canonicalized tag spec (which describes a single, possibly multipart tag) into the displayed form. The tag spec may be a string (a canonical-form tag); a list of canonical-form tags (in the case of a simple multipart tag); or a list of mixed canonical-form tags and lists of such tags (in the case of a two-level multipart tag). `joiner` indicates how to join the parts of a multipart tag, and can be either {"and"} ("foo and bar", or "foo, bar and baz" for 3 or more), {"slash"} ("foo/bar"), {"en-dash"} ("foo–bar") or {nil}, which uses the global default found in {multipart_join_strategy()} in [[Module:form of/functions]]. (NOTE: The global default is {"slash"} and this seems unlikely to change.) A truthy `nolink` suppresses linking.]==] function export.get_tag_display_form(tagspec, lang, joiner, nolink) if type(tagspec) == "string" then return get_single_tag_display_form(tagspec, lang, nolink) end -- We have a multipart tag. See if there's a display handler to display them specially. for _, handler in ipairs(display_handlers or get_display_handlers()) do local displayval = handler(tagspec, joiner) if displayval then if nolink then displayval = remove_links(displayval) end return displayval end end -- No display handler. local displayed_tags = {} for _, first_level_tag in ipairs(tagspec) do if type(first_level_tag) == "string" then insert(displayed_tags, get_single_tag_display_form(first_level_tag, lang, nolink)) else -- A first-level element of a two-level multipart tag. Currently we just separate the individual components -- with spaces, but other ways are possible, e.g. using an underscore, colon, parens or braces. local components = {} for _, component in ipairs(first_level_tag) do insert(components, get_single_tag_display_form(component, lang, nolink)) end insert(displayed_tags, concat(components, " ")) end end return join_multiparts(displayed_tags, joiner) end get_tag_display_form = export.get_tag_display_form --[==[ Given a normalized tag set (i.e. as output by {normalize_tag_set()}; all tags are in canonical form, multipart tags are represented as lists, and two-level multipart tags as lists of lists), convert to displayed form (a string). See {get_tag_display_form()} for the meaning of `joiner`. A truthy `nolink` suppresses linking.]==] function export.get_tag_set_display_form(normalized_tag_set, lang, joiner, nolink) local parts = {} for _, tagspec in ipairs(normalized_tag_set) do local to_insert = get_tag_display_form(tagspec, lang, joiner, nolink) -- Maybe insert a space before inserting the display form of the tag. We insert a space if -- (a) we're not the first tag; and -- (b) the tag we're about to insert doesn't have the "no_space_on_left" property; and -- (c) the preceding tag doesn't have the "no_space_on_right" property. -- NOTE: We depend here on the fact that -- (1) all tags with either of the above properties set have the same display form as canonical form, and -- (2) all tags with either of the above properties set are single-character tags. -- The second property is an optimization to avoid looking up display forms resulting from multipart tags, -- which won't be found and which will trigger loading of [[Module:form of/data/2]]. If multichar punctuation is -- added in the future, it's ok to change the == 1 below to <= 2 or <= 3. -- -- If the first property above fails to hold in the future, we need to track the canonical form of each tag -- (including the previous one) as well as the display form. This would also avoid the need for the == 1 check. if #parts > 0 then local most_recent_tagobj = parts[#parts]:match("^.[\128-\191]*$") and lookup_tag(parts[#parts], lang) local to_insert_tagobj = to_insert:match("^.[\128-\191]*$") and lookup_tag(to_insert, lang) if ( (not most_recent_tagobj or not most_recent_tagobj.no_space_on_right) and (not to_insert_tagobj or not to_insert_tagobj.no_space_on_left) ) then insert(parts, " ") end end insert(parts, to_insert) end return concat(parts) end get_tag_set_display_form = export.get_tag_set_display_form --[==[ Split a tag set containing two-level multipart tags into one or more tag sets not containing such tags. Single-level multipart tags are left alone. (If we need to, a slight modification of the following code will also split single-level multipart tags.) This assumes that multipart tags are represented as lists and two-level multipart tags are represented as lists of lists, as is output by {normalize_tag_set()}. NOTE: We have to be careful to properly handle imbalanced two-level multipart tags such as `def:s//p` (or the reverse, `s//def:p`).]==] local function split_two_level_multipart_tag_set(tag_set) for i, tag in ipairs(tag_set) do if type(tag) == "table" then -- We saw a multipart tag. Check if any of the parts are two-level. local saw_two_level_tag = false for _, first_level_tag in ipairs(tag) do if type(first_level_tag) == "table" then saw_two_level_tag = true break end end if saw_two_level_tag then -- We found a two-level multipart tag. -- (1) Extract the preceding tags. local pre_tags = slice(tag_set, 1, i - 1) -- (2) Extract the following tags. local post_tags = slice(tag_set, i + 1) -- (3) Loop over each tag set alternant in the two-level multipart tag. -- For each alternant, form the tag set consisting of pre_tags + alternant + post_tags, -- and recursively split that tag set. local resulting_tag_sets = {} for _, first_level_tag_set in ipairs(tag) do local expanded_tag_set = {} extend(expanded_tag_set, pre_tags) -- The second level may have a string or a list. if type(first_level_tag_set) == "table" then extend(expanded_tag_set, first_level_tag_set) else insert(expanded_tag_set, first_level_tag_set) end extend(expanded_tag_set, post_tags) extend(resulting_tag_sets, split_two_level_multipart_tag_set(expanded_tag_set)) end return resulting_tag_sets end end end return {tag_set} end local function try_lang_specific_module(langcode, modules_tried, name, data) local lang_specific_module = form_of_lang_data_module_prefix .. langcode .. "/functions" local langdata = safe_require(lang_specific_module) if langdata then insert(modules_tried, lang_specific_module) if langdata.cat_functions then local fn = langdata.cat_functions[name] if fn then return fn(data), true end end end return nil, false end -- Call a named function, either from the lang-specific data in -- [[Module:form of/lang-specific/LANGCODE/functions]] or in [[Module:form of/functions]]. local function call_named_function(name, funtype, normalized_tag_set, lang, POS, pagename, lemmas) local data = { pagename = pagename or default_pagename or get_default_pagename(), lemmas = lemmas, tag_set = normalized_tag_set, lang = lang, POS = POS } local modules_tried = {} -- First try lang-specific. while lang do local retval, found_it = try_lang_specific_module(lang:getCode(), modules_tried, name, data) if found_it then return retval end -- If the language has a parent (i.e. a superordinate variety), try again with that. lang = lang:getParent() end -- Try lang-independent. insert(modules_tried, form_of_functions_module) local fn = (cat_functions or get_cat_functions())[name] if fn then return fn(data) end for i, modname in ipairs(modules_tried) do modules_tried[i] = "[[" .. modname .. "]]" end error(("No %s function named '%s' in %s"):format(funtype, name, list_to_text(modules_tried, nil, " or "))) end -- Given a tag from the current tag set (which may be a list in case of a multipart tag), -- and a tag from a categorization spec, check that the two match. -- (1) If both are strings, we just check for equality. -- (2) If the spec tag is a string and the tag set tag is a list (i.e. it originates from a -- multipart tag), we check that the spec tag is in the list. This is because we want to treat -- multipart tags in user-specified tag sets as if the user had specified multiple tag sets. -- For example, if the user said "1//3|s|pres|ind" and the categorization spec says {"has", "1"}, -- we want this to match, because "1//3|s|pres|ind" should be treated equivalently to two tag -- sets "1|s|pres|ind" and "3|s|pres|ind", and the former matches the categorization spec. -- (3) If the spec tag is a list (i.e. it originates from a multipart tag), we check that the -- tag set tag is also a list and is a superset of the spec tag. For example, if the categorization -- spec says {"has", "1//3"}, then the tag set tag must be a multipart tag that has both "1" and "3" -- in it. "1//3" works, as does "1//2//3". local function tag_set_tag_matches_spec_tag(tag_set_tag, spec_tag) if type(spec_tag) == "table" then if type(tag_set_tag) == "table" and is_subset_list(spec_tag, tag_set_tag) then return true end elseif type(tag_set_tag) == "table" then if contains(tag_set_tag, spec_tag) then return true end elseif tag_set_tag == spec_tag then return true end return false end -- Check that the current tag set matches the given spec tag. This means that any of the tags -- in the current tag set match, according to tag_set_tag_matches_spec_tag(); see above. If the -- current tag set contains only string tags (i.e. no multipart tags), and the spec tag is a -- string (i.e. not a multipart tag), this boils down to list containment, but it gets more -- complex when multipart tags are present. local function tag_set_matches_spec_tag(spec_tag, tag_set, lang) spec_tag = normalize_tag(spec_tag, lang) for _, tag_set_tag in ipairs(tag_set) do if tag_set_tag_matches_spec_tag(tag_set_tag, spec_tag) then return true end end return false end -- Check whether the given spec matches the current tag set. Two values are returned: -- (1) whether the spec matches the tag set; (2) the index of the category to add if -- the spec matches. local function check_condition(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas) if type(spec) == "boolean" then return spec elseif type(spec) ~= "table" then error("Wrong type of condition " .. spec .. ": " .. type(spec)) end local predicate = spec[1] if predicate == "has" then return tag_set_matches_spec_tag(spec[2], tag_set, lang), 3 elseif predicate == "hasall" then for _, tag in ipairs(spec[2]) do if not tag_set_matches_spec_tag(tag, tag_set, lang) then return false, 3 end end return true, 3 elseif predicate == "hasany" then for _, tag in ipairs(spec[2]) do if tag_set_matches_spec_tag(tag, tag_set, lang) then return true, 3 end end return false, 3 elseif predicate == "tags=" then local normalized_spec_tag_sets = normalize_tag_set(spec[2], lang) if #normalized_spec_tag_sets > 1 then error("Internal error: No support for conjoined shortcuts in category/label specs in " .. "[[Module:form of/cats]] when processing spec tag set " .. concat(spec[2], "|")) end local normalized_spec_tag_set = normalized_spec_tag_sets[1] -- Check for and disallow two-level multipart tags in the specs. FIXME: Remove this when we remove -- support for two-level multipart tags. for _, tag in ipairs(normalized_spec_tag_set) do if type(tag) == "table" then for _, subtag in ipairs(tag) do if type(subtag) == "table" then error("Internal error: No support for two-level multipart tags in category/label specs" .. "[[Module:form of/cats]] when processing spec tag set " .. concat(spec[2], "|")) end end end end -- Allow tags to be in different orders, and multipart tags to be in different orders. To handle this, -- we first check that both tag set tags and spec tags have the same length. If so, we sort the -- multipart tags in the tag set tags and spec tags, and then check that all tags in the spec tags are -- in the tag set tags. if #tag_set ~= #normalized_spec_tag_set then return false, 3 end local tag_set_tags = deep_copy(tag_set) for i=1,#tag_set_tags do if type(tag_set_tags[i]) == "table" then sort(tag_set_tags[i]) end if type(normalized_spec_tag_set[i]) == "table" then sort(normalized_spec_tag_set[i]) end end for i=1,#tag_set_tags do if not contains(tag_set_tags, normalized_spec_tag_set[i]) then return false, 3 end end return true, 3 elseif predicate == "p=" then return POS == normalize_pos(spec[2]), 3 elseif predicate == "pany" then for _, specpos in ipairs(spec[2]) do if POS == normalize_pos(specpos) then return true, 3 end end return false, 3 elseif predicate == "pexists" then return POS ~= nil, 2 elseif predicate == "not" then local condval = check_condition(spec[2], tag_set, normalized_tag_set, lang, POS, pagename, lemmas) return not condval, 3 elseif predicate == "and" then local condval = check_condition(spec[2], tag_set, normalized_tag_set, lang, POS, pagename, lemmas) if condval then condval = check_condition(spec[3], tag_set, normalized_tag_set, lang, POS, pagename, lemmas) end return condval, 4 elseif predicate == "or" then local condval = check_condition(spec[2], tag_set, normalized_tag_set, lang, POS, pagename, lemmas) if not condval then condval = check_condition(spec[3], tag_set, normalized_tag_set, lang, POS, pagename, lemmas) end return condval, 4 elseif predicate == "call" then return call_named_function(spec[2], "condition", normalized_tag_set, lang, POS, pagename, lemmas), 3 else error("Unrecognized predicate: " .. predicate) end end -- Process a given spec. This checks any conditions in the spec against the -- tag set, and insert any resulting categories into `categories`. Return value -- is true if the outermost condition evaluated to true and a category was inserted -- (this is used in {"cond" ...} conditions, which stop when a subcondition evaluates -- to true). local function process_spec(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) if not spec then return false elseif type(spec) == "string" then -- A category. Substitute POS request with user-specified part of speech or default. spec = spec:gsub("<<p=(.-)>>", function(default) return POS or normalize_pos(default) end) insert(categories, lang:getFullName() .. " " .. spec) return true elseif type(spec) == "table" and spec.labels then -- A label spec. for _, label in ipairs(spec.labels) do insert_if_not(labels, label) end return true elseif type(spec) ~= "table" then error("Wrong type of specification " .. spec .. ": " .. type(spec)) end local predicate = spec[1] if predicate == "multi" then for _, sp in iterate_from(2, ipairs(spec)) do -- Iterate from 2. process_spec(sp, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) end return true elseif predicate == "cond" then for _, sp in iterate_from(2, ipairs(spec)) do -- Iterate from 2. if process_spec(sp, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) then return true end end return false elseif predicate == "call" then return process_spec( call_named_function(spec[2], "spec", normalized_tag_set, lang, POS, pagename, lemmas), tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels ) else local condval, ifspec = check_condition(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas) if condval then process_spec(spec[ifspec], tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) return true else process_spec(spec[ifspec + 1], tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) -- FIXME: Are we sure this is correct? return false end end end --[==[ Given a normalized tag set (i.e. as output by {normalize_tag_set()}; all tags are in canonical form, multipart tags are represented as lists, and two-level multipart tags as lists of lists), fetch the associated categories and labels. Return two values, a list of categories and a list of labels. `lang` is the language of term represented by the tag set, and `POS` is the user-provided part of speech (which may be {nil}).]==] function export.fetch_categories_and_labels(normalized_tag_set, lang, POS, pagename, lemmas) local categories, labels = {}, {} POS = normalize_pos(POS) -- First split any two-level multipart tags into multiple sets, to make our life easier. for _, tag_set in ipairs(split_two_level_multipart_tag_set(normalized_tag_set)) do local langcode = lang:getCode() local langspecs = (m_cats_data or get_m_cats_data())[langcode] if langspecs then for _, spec in ipairs(langspecs) do process_spec(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) end end local full_code = lang:getFullCode() if full_code ~= langcode then local langspecs = (m_cats_data or get_m_cats_data())[full_code] if langspecs then for _, spec in ipairs(langspecs) do process_spec(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) end end end if full_code ~= "und" then local langspecs = (m_cats_data or get_m_cats_data())["und"] if langspecs then for _, spec in ipairs(langspecs) do process_spec(spec, tag_set, normalized_tag_set, lang, POS, pagename, lemmas, categories, labels) end end end end return categories, labels end fetch_categories_and_labels = export.fetch_categories_and_labels local function format_labels(labels, data, notext) if labels and #labels > 0 then return show_labels{ labels = labels, lang = data.lang, sort = data.sort, nocat = data.nocat } .. (notext and (data.pretext or "") == "" and "" or " ") else return "" end end --[==[ Implementation of templates that display inflection tags, such as the general {{tl|inflection of}}, semi-specific variants such as {{tl|participle of}}, and specific variants such as {{tl|past participle of}}. `data` contains all the information controlling the display, with the following fields: * `.lang`: ('''''required''''') Language to use when looking up language-specific inflection tags, categories and labels, and for displaying categories and labels. * `.tags`: ('''''required''' unless `.tag_sets` is given'') List of non-canonicalized inflection tags. Multiple tag sets can be indicated by a {";"} as one of the tags, and tag-set properties may be attached to the last tag of a tag set. The tags themselves may come directly from the user (as in {{tl|inflection of}}); come partly from the user (as in {{tl|participle of}}, which adds the tag `part` to user-specified inflection tags); or be entirely specified by the template (as in {{tl|past participle of}}). * `.tag_sets`: ('''''required''' unless `.tags` is given'') List of non-canonicalized tag sets and associated per-tag-set properties. Each element of the list is an object of the form { {tags = {"TAG", "TAG", ...}, labels = {"LABEL", "LABEL", ...}}. If `.tag_sets` is specified, `.tags` should not be given and vice-versa. Specifying `.tag_sets` in place of tags allowed per-tag set labels to be specified; otherwise, there is no advantage. [[Module:pt-gl-inflections]] uses this functionality to supply labels like {"Brazil"} and {"Portugal"} associated with specific tag sets. * `.lemmas`: ('''''recommended''''') List of objects describing the lemma(s) of which the term in question is a non-lemma form. These are passed directly to {full_link()} in [[Module:links]]. Each object should have at minimum a `.lang` field containing the language of the lemma and a `.term` field containing the lemma itself. Each object is formatted using {full_link()} and then if there are more than one, they are joined using {serialCommaJoin()} in [[Module:table]]. Alternatively, `.lemmas` can be a string, which is displayed directly. If omitted entirely, no lemma links are shown and the connecting "of" is also omitted. * `.lemma_face`: ('''''recommended''''') "Face" to use when displaying the lemma objects. Usually should be set to {"term"}. * `.POS`: ('''''recommended''''') Categorizing part-of-speech tag. Comes from the {{para|p}} or {{para|POS}} argument of {{tl|inflection of}}. * `.pagename`: Page name of "current" page or nil to use the actual page title; for testing purposes. * `.conj`: Conjunction or separator to use when joining multiple lemma objects. Defaults to {"and"}. * `.enclitics`: List of enclitics to display after the lemmas, in parens. * `.enclitic_conj`: Conjunction or separator to use when joining multiple enclitics. Defaults to {"and"}. * `.no_format_categories`: If true, don't format the categories derived from the inflection tags; just return them. * `.sort`: Sort key for formatted categories. Ignored when `.no_format_categories` = {true}. * `.nocat`: Suppress computation of categories (even if `.no_format_categories` is not given). * `.notext`: Disable display of all tag text and `inflection of` text. (FIXME: Maybe not implemented correctly.) * `.nolink`: Suppress linking of terms in inflection tags. * `.capfirst`: Capitalize the first word displayed. * `.pretext`: Additional text to display before the inflection tags, but after any top-level labels. * `.posttext`: Additional text to display after the lemma links. * `.text_classes`: CSS classes used to wrap the tag text and lemma links. Default is {"form-of-definition use-with-mention"}. * `.lemma_classes`: Additional CSS classes used to wrap the lemma links. Default is {"form-of-definition-link"}. * `.joiner`: Override the joiner (normally a slash) used to join multipart tags. You should normally not specify this. * `.nowrap`: Do not wrap the form-of definitions in a span, unless `.text_classes` is specified. * `.ok_to_destructively_modify`: If set, data structures (including the nested lemma structures) can be modified in-place to save memory; otherwise they will be copied before modifying. A typical call might look like this (for {{m+|es|amo}}): { local lang = require("Module:languages").getByCode("es") local lemma_obj = { lang = lang, term = "amar", } return m_form_of.tagged_inflections({ lang = lang, tags = {"1", "s", "pres", "ind"}, lemmas = {lemma_obj}, lemma_face = "term", POS = "verb" }) } Normally, one value is returned, the formatted text, which has appended to it the formatted categories derived from the tag-set-related categories generated by the specs in [Module:form of/cats]]. To suppress this, set `data.no_format_categories` = {true}, in which case two values are returned, the formatted text without any formatted categories appended and a list of the categories to be formatted. NOTE: There are two sets of categories that may be generated: (1) categories derived directly from the tag sets, as specified in [[Module:form of/cats]]; (2) categories derived from tag-set labels, either (a) set explicitly by the caller in `data.tag_sets`, (b) specified by the user using `<lb:...>` attached to the last tag in a tag set, or (c) specified in [[Module:form of/cats]]. The second type (label-related categories) are currently not returned in the second return value of {tagged_inflections()}, and are currently inserted into the output text even if `data.no_format_categories` is set to {true}; but they can be suppressed by setting `data.nocat` = {true} (which also suppresses the first type of categories, those derived directly from tag sets, even if `data.no_format_categories` is set to {true}).]==] function export.tagged_inflections(data) if not data.tags and not data.tag_sets then error("First argument must be a table of arguments, and `.tags` or `.tag_sets` must be specified") end if data.tags and data.tag_sets then error("Both `.tags` and `.tag_sets` cannot be specified") end local tag_sets = data.tag_sets if not tag_sets then tag_sets = split_tag_set(data.tags) for i, tag_set in ipairs(tag_sets) do tag_sets[i] = parse_tag_set_properties(tag_set) end end local inflections = {} local categories = {} for _, tag_set in ipairs(tag_sets) do local normalized_tag_sets = normalize_tag_set(tag_set.tags, data.lang, "do-track") for _, normalized_tag_set in ipairs(normalized_tag_sets) do local this_categories, this_labels = fetch_categories_and_labels(normalized_tag_set, data.lang, data.POS, data.pagename, type(data.lemmas) == "table" and data.lemmas or nil) if not data.nocat then extend(categories, this_categories) end local cur_infl = get_tag_set_display_form(normalized_tag_set, data.lang, data.joiner, data.nolink) if #cur_infl > 0 then if tag_set.labels then this_labels = append(tag_set.labels, this_labels) end insert(inflections, {infl_text = cur_infl, labels = this_labels}) end end end local overall_labels, need_per_tag_set_labels for _, inflection in ipairs(inflections) do if overall_labels == nil then overall_labels = inflection.labels elseif not deep_equals(overall_labels, inflection.labels) then need_per_tag_set_labels = true overall_labels = nil break end end if not need_per_tag_set_labels then for _, inflection in ipairs(inflections) do inflection.labels = nil end end local format_data = shallow_copy(data) local of_text = data.lemmas and " of" or "" local formatted_text, this_categories if #inflections == 1 then if need_per_tag_set_labels then error("Internal error: need_per_tag_set_labels should not be set with one inflection") end format_data.text = format_labels(overall_labels, data, data.notext) .. (data.pretext or "") .. (data.notext and "" or ((data.capfirst and ucfirst(inflections[1].infl_text) or inflections[1].infl_text) .. of_text)) formatted_text, this_categories = format_form_of(format_data) else format_data.text = format_labels(overall_labels, data, data.notext) .. (data.pretext or "") .. (data.notext and "" or ((data.capfirst and "Inflection" or "inflection") .. of_text)) format_data.posttext = (data.posttext or "") .. ":" local link link, this_categories = format_form_of(format_data) local text_classes = data.text_classes if text_classes == nil and not data.nowrap then text_classes = "form-of-definition use-with-mention" end for i, inflection in ipairs(inflections) do inflections[i] = "\n## " .. format_labels(inflection.labels, data, false) .. wrap_in_span(inflection.infl_text, text_classes) end formatted_text = link .. concat(inflections) end if this_categories[1] then extend(categories, this_categories) end if not data.no_format_categories then if categories[1] then formatted_text = formatted_text .. format_categories(categories, data.lang, data.sort, nil, export.force_cat) end return formatted_text end return formatted_text, categories end function export.dump_form_of_data(_frame) local data = { require(form_of_data1_module), require(form_of_data2_module) } return require(json_module).toJSON(data) end export.form_of_cats_module = form_of_cats_module export.form_of_data1_module = form_of_data1_module export.form_of_data2_module = form_of_data2_module export.form_of_functions_module = form_of_functions_module export.form_of_lang_data_module_prefix = form_of_lang_data_module_prefix export.headword_data_module = headword_data_module -- so all form-of modules stay in sync return export 537y0z5vf78rgvzdvy0emj66k9afncd Module:gender and number 828 7988 54734 2026-09-25T21:17:28Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local decorations_module = "Module:decorations" local load_module = "Module:load" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function format_...' 54734 Scribunto text/plain local export = {} local decorations_module = "Module:decorations" local load_module = "Module:load" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local gender_and_number_data local function get_gender_and_number_data() gender_and_number_data, get_gender_and_number_data = load_data("Module:gender and number/data"), nil return gender_and_number_data end --[==[ intro: This module creates standardised displays for gender and number. It converts a gender specification into Wiki/HTML format. A gender/number specification consists of one or more gender/number elements, separated by hyphens. Examples are: {"n"} (neuter gender), {"f-p"} (feminine plural), {"m-an-p"} (masculine animate plural), {"pf"} (perfective aspect). Each gender/number element has the following properties: # A code, as used in the spec, e.g. {"f"} for feminine, {"p"} for plural. # A type, e.g. `gender`, `number` or `animacy`. Each element in a given spec must be of a different type. # A display form, which in turn consists of a display code and a tooltip gloss. The display code may not be the same as the spec code, e.g. the spec code {"an"} has display code {"anim"} and tooltip gloss ''animate''. # A category into which lemmas of the right part of speech are placed if they have a gender/number spec containing the given element. For example, a noun with gender/number spec {"m-an-p"} is placed into the categories `<var>lang</var> masculine nouns`, `<var>lang</var> animate nouns` and `<var>lang</var> pluralia tantum`. ]==] --[==[ Version of format_genders() that can be invoked from a template. ]==] function export.show_list(frame) local params = { [1] = {list = true}, ["lang"] = {type = "language"}, } local iargs = process_params(frame.args, params) local text, cats = export.format_genders(iargs[1], iargs.lang) if not cats then return text end return text .. format_categories(cats, iargs.lang) end function export.format_list(...) -- FIXME: Added 2026-09-17. Remove after 2026-10-17. error("Use format_genders instead of format_list") end local function autoadd_abbr(display) if not display then error("Internal error: '.display' for gender/number code is missing") end if display:find("<abbr", nil, true) then return display end return ('%s'):format(display, display) end --[=[ Add decorations (qualifiers, labels and references) to a formatted gender/number spec. `spec` is the object describing the gender/number spec, which should optionally contain: * left qualifiers in `q`, an array of strings; * right qualifiers in `qq`, an array of strings; * left labels in `l`, an array of strings; * right labels in `ll`, an array of strings; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; `formatted` is the formatted version of the term itself, and `lang` is the optional language object passed into format_genders(). ]=] local function add_decorations(formatted, spec, lang) local function field_non_empty(field) local list = spec[field] if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `spec.%s`=%s, should be \"table\""):format( field, mw.dumpObject(list))) end return list[1] end if field_non_empty("q") or field_non_empty("qq") or field_non_empty("l") or field_non_empty("ll") or field_non_empty("refs") then formatted = format_decorations { lang = lang, text = formatted, q = spec.q, qq = spec.qq, l = spec.l, ll = spec.ll, refs = spec.refs, } end return formatted end --[==[ Format one or more gender/number specifications. Each spec is either a string, e.g. {"f-p"}, or a table of the form { {spec = "SPEC", q = {"LEFT_QUALIFIER", "LEFT_QUALIFIER", ...}, qq = {"RIGHT_QUALIFIER", "RIGHT_QUALIFIER", ...}, l = {"LEFT_LABEL", "LEFT_LABEL", ...}, ll = {"RIGHT_LABEL", "RIGHT_LABEL", ...}, refs = {"FORMATTED_REFERENCE_TEXT", {text = "FORMATTED_REFERENCE_TEXT", name = "REFNAME", group = "REFGROUP"}, ...}, }} In the table format, only `spec` is required, and in the `refs` object format, only `text` is required. If `lang` is not given, the funtion returns only one value, the formatted text. If `lang` is given, the function returns two values: # the formatted text; # a list of the categories to add, which will be {nil} if there are no categories to add. If `lang` (which should be a language object) and `pos_for_cat` (which should be a plural part of speech) are given, gender categories such as `German masculine nouns` or `Russian imperfective verbs` are added to the categories, and request categories such as `Requests for gender in <var>lang</var> entries` or `Requests for animacy in <var>lang</var> entries` may also be added. Otherwise, if only `lang` is given, only request categories may be returned. If both are omitted, only one return value is returned, as mentioned above. ]==] function export.format_genders(specs, lang, pos_for_cat) local formatted_specs, categories, seen_types = {} local all_is_nounclass = nil local full_langname = lang and lang:getFullName() or nil local function do_gender_spec(spec, parts) local types = {} local codes = (gender_and_number_data or get_gender_and_number_data()).codes for key, code in ipairs(parts) do -- Is this code valid? if not codes[code] then error('The tag "' .. code .. '" in the gender specification "' .. spec.spec .. '" is not valid. See [[Module:gender and number]] for a list of valid tags.') end -- Check for multiple genders/numbers/animacies in a single spec. local typ = codes[code].type if typ ~= "other" and types[typ] then error('The gender specification "' .. spec.spec .. '" contains multiple tags of type "' .. typ .. '".') end types[typ] = true parts[key] = autoadd_abbr(codes[code].display) -- Generate categories if called for. if lang and pos_for_cat then local cat = codes[code].cat if cat then if not categories then categories = {} end insert(categories, full_langname .. " " .. cat) end if not seen_types then seen_types = {} elseif seen_types[typ] and seen_types[typ] ~= code then cat = (gender_and_number_data or get_gender_and_number_data()).multicode_cats[typ] if cat then if not categories then categories = {} end insert(categories, full_langname .. " " .. cat) end end seen_types[typ] = code end if lang and codes[code].req then local type_for_req = typ if code == "?" then -- Keep in mind `pos_for_cat` may be nil here. type_for_req = pos_for_cat == "verbs" and "aspect" or "gender" end if not categories then categories = {} end insert(categories, "Requests for " .. type_for_req .. " in " .. full_langname .. " entries") end end -- Add the processed codes together with non-breaking spaces if not parts[2] and parts[1] then return parts[1] else return concat(parts, "&nbsp;") end end for _, spec in ipairs(specs) do if type(spec) ~= "table" then spec = {spec = spec} end local spec_spec, is_nounclass = spec.spec -- If the specification starts with cX, then it is a noun class specification. if spec_spec:match("^%d") or spec_spec:match("^c[^-]") then is_nounclass = true local code = spec_spec:gsub("^c", "") local text if code == "?" then text = '<abbr class="noun-class" title="noun class missing">?</abbr>' if lang then if not categories then categories = {} end insert(categories, "Requests for noun class in " .. full_langname .. " entries") end else text = '<abbr class="noun-class" title="noun class ' .. code .. '">' .. code .. "</abbr>" if lang and pos_for_cat then if not categories then categories = {} end insert(categories, full_langname .. " class " .. code .. " POS") end end local text_with_decorations = add_decorations(text, spec, lang) insert(formatted_specs, text_with_decorations) else -- Split the parts and iterate over each part, converting it into its display form local parts = split(spec.spec, "-", true, true) local combined_codes = (gender_and_number_data or get_gender_and_number_data()).combinations if lang then -- Check if the specification is valid --elseif langinfo.genders then -- local valid_genders = {} -- for _, g in ipairs(langinfo.genders) do valid_genders[g] = true end -- -- if not valid_genders[spec.spec] then -- local valid_string = {} -- for i, g in ipairs(langinfo.genders) do valid_string[i] = g end -- error('The gender specification "' .. spec.spec .. '" is not valid for ' .. langinfo.names[1] .. ". Valid are: " .. concat(valid_string, ", ")) -- end --end end local has_combined = false for _, code in ipairs(parts) do if combined_codes[code] then has_combined = true break end end if not has_combined then if formatted_specs[1] then insert(formatted_specs, "or") end insert(formatted_specs, add_decorations(do_gender_spec(spec, parts), spec, lang)) else -- This logic is to handle combined gender specs like 'mf' and 'mfbysense'. local all_parts = {{}} local extra_displays local this_formatted_specs = {} for _, code in ipairs(parts) do if combined_codes[code] then local new_all_parts = {} for _, one_parts in ipairs(all_parts) do for _, one_code in ipairs(combined_codes[code].codes) do local new_combined_parts = deep_copy(one_parts) insert(new_combined_parts, one_code) insert(new_all_parts, new_combined_parts) end end all_parts = new_all_parts if lang and pos_for_cat then local extra_cat = combined_codes[code].cat if extra_cat then if not categories then categories = {} end insert(categories, full_langname .. " " .. extra_cat) end end local extra_display = combined_codes[code].display if extra_display then if not extra_displays then extra_displays = {} end insert(extra_displays, autoadd_abbr(extra_display)) end else for _, one_parts in ipairs(all_parts) do insert(one_parts, code) end end end for _, this_parts in ipairs(all_parts) do if this_formatted_specs[1] then insert(this_formatted_specs, "or") end insert(this_formatted_specs, do_gender_spec(spec, this_parts)) end if extra_displays then for _, display in ipairs(extra_displays) do insert(this_formatted_specs, display) end end insert(formatted_specs, add_decorations( concat(this_formatted_specs, " "), spec, lang)) end is_nounclass = false end -- Ensure that the specifications are either all noun classes, or none are. if all_is_nounclass == nil then all_is_nounclass = is_nounclass elseif all_is_nounclass ~= is_nounclass then error("Noun classes and genders cannot be mixed. Please use either one or the other.") end end if categories and lang and pos_for_cat then for i, cat in ipairs(categories) do categories[i] = cat:gsub("POS", pos_for_cat) end end local formatted_retval if all_is_nounclass then -- Add the processed codes together with slashes formatted_retval = '<span class="gender">class ' .. concat(formatted_specs, "/") .. "</span>" else -- Add the processed codes together with spaces formatted_retval = '<span class="gender">' .. concat(formatted_specs, " ") .. "</span>" end if lang then return formatted_retval, categories else return formatted_retval end end return export dvsfo9jjtypkiipvmtymidcy88tah1n Module:string/patternEscape 828 7989 54736 2026-09-25T21:24:44Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local gsub = string.gsub local chars local function get_chars() chars, get_chars = { ["\000"] = "%z", ["$"] = "%$", ["%"] = "%%", ["("] = "%(", [")"] = "%)", ["*"] = "%*", ["+"] = "%+", ["-"] = "%-", ["."] = "%.", ["?"] = "%?", ["["] = "%[", ["]"] = "%]", ["^"] = "%^", }, nil return chars end --[==[Escapes the magic characters used in a pattern: {$%()*+-.?[]^}, and converts the null character to {%z}. For example, {"^$()%.[]*+-?\0"} becomes {"%^%$%(%)%...' 54736 Scribunto text/plain local gsub = string.gsub local chars local function get_chars() chars, get_chars = { ["\000"] = "%z", ["$"] = "%$", ["%"] = "%%", ["("] = "%(", [")"] = "%)", ["*"] = "%*", ["+"] = "%+", ["-"] = "%-", ["."] = "%.", ["?"] = "%?", ["["] = "%[", ["]"] = "%]", ["^"] = "%^", }, nil return chars end --[==[Escapes the magic characters used in a pattern: {$%()*+-.?[]^}, and converts the null character to {%z}. For example, {"^$()%.[]*+-?\0"} becomes {"%^%$%(%)%%%.%[%]%*%+%-%?%z"}. This is necessary when constructing a pattern involving arbitrary text (e.g. from user input).]==] return function(str) return (gsub(str, "[%z$%%()*+%-.?[%]^]", chars or get_chars())) end kdhwp5dohwvh931huz7m0oufqqh05wl Module:string/replacementEscape 828 7990 54737 2026-09-25T21:25:16Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local gsub = string.gsub --[==[Escapes {%}, which is the only magic character used in replacement strings, which are given as the third argument to {string.gsub} and {mw.ustring.gsub}, as well as format strings given to {string.format} and {mw.ustring.format}.]==] return function(str) return (gsub(str, "%%", "%%%%")) end' 54737 Scribunto text/plain local gsub = string.gsub --[==[Escapes {%}, which is the only magic character used in replacement strings, which are given as the third argument to {string.gsub} and {mw.ustring.gsub}, as well as format strings given to {string.format} and {mw.ustring.format}.]==] return function(str) return (gsub(str, "%%", "%%%%")) end aid90he3azqkc4qrytnczmfsjczbfe3 Module:array 828 7991 54740 2026-09-25T21:37:58Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local debug_track_module = "Module:debug/track" local function_module = "Module:fun" local table_module = "Module:table" local get_array_mt -- Defined below. local getmetatable = getmetatable local ipairs = ipairs local pairs = pairs local rawget = rawget local rawset = rawset local require = require local select = select local setmetatable = setmetatable local sort = table.sort local type = type local unpack = unpack or table.unpack -- Lua 5....' 54740 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local function_module = "Module:fun" local table_module = "Module:table" local get_array_mt -- Defined below. local getmetatable = getmetatable local ipairs = ipairs local pairs = pairs local rawget = rawget local rawset = rawset local require = require local select = select local setmetatable = setmetatable local sort = table.sort local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local upper = string.upper local array_mt -- Defined below. --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function append(...) append = require(table_module).append return append(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local m_function local function get_m_function() m_function, get_m_function = require(function_module), nil return m_function end local m_table local function get_m_table() m_table, get_m_table = require(table_module), nil return m_table end -- Functions from [[Module:table]] that operate on arrays or sparse arrays. -- List copied from [[Module:table/documentation]]. local m_table_array_funcs local function get_m_table_array_funcs() m_table_array_funcs = list_to_set{ -- non-sparse "signedIndex", "append", "extend", "extendIfNot", "slice", "removeDuplicates", "length", "size", "contains", "serialCommaJoin", "reverseIpairs", "reverse", "invert", "listToSet", "flatten", "isArray", -- sparse "numKeys", "maxIndex", "compressSparseArray", "indexPairs", "indexIpairs", "sparseIpairs", -- tables in general "shallowCopy", "deepCopy" } get_m_table_array_funcs = nil return m_table_array_funcs end -- Functions from [[Module:fun]] that take an array in the second argument. -- They just have to have the argument order reversed to work as methods of the -- array object. local m_function_array_funcs local function get_m_function_array_funcs() m_function_array_funcs = list_to_set{ "map", "some", "all", "filter", "fold" } get_m_function_array_funcs = nil return m_function_array_funcs end -- Functions from [[Module:table]] that create an array or table. -- Not all of these operate on arrays. local m_table_new_array_funcs local function get_m_table_new_array_funcs() m_table_new_array_funcs = list_to_set{ -- Array. "append", "slice", "removeDuplicates", "numKeys", "compressSparseArray", "keysToList", "reverse", "flatten", -- Array or table. "shallowCopy", "deepCopy" } get_m_table_new_array_funcs = nil return m_table_new_array_funcs end -- Functions from [[Module:fun]] that create an array or table. -- Not all of these operate on arrays. local m_function_new_array_funcs local function get_m_function_new_array_funcs() m_function_new_array_funcs = list_to_set{ "map", "filter", } get_m_function_new_array_funcs = nil return m_function_new_array_funcs end -- Add aliases for the functions from [[Module:table]] whose names -- contain "array" or "list", which is redundant. -- The key redirects to the value. local alias_of = { compress = "compressSparseArray", keys = "keysToList", toSet = "listToSet", } local function underscore_to_camel_case(str) if type(str) ~= "string" then return str end local ret = str:gsub("_(.)", upper) if ret ~= str then debug_track("array/underscore to camel case") end return ret end local function get_module_function(key, module, module_name) return module[key] or error(("Cannot find %s in [[Module:%s]]"):format(mw.dumpObject(key), module_name)) end local function wrap_in_array_constructor(func) return function (...) return setmetatable(func(...), array_mt or get_array_mt()) end end function get_array_mt() -- Copy table library so as not to unexpectedly change the behavior of code that -- uses it. local Array = deep_copy(table) Array.ipairs = ipairs Array.pairs = pairs Array.unpack = unpack Array.listToText = mw.text.listToText -- Create version of table.sort that returns the table. function Array:sort(comp) sort(self, comp) return self end function Array:type() local mt = getmetatable(self) return mt and type(mt) == "table" and rawget(mt, "__type") or nil end local Array_mt = {} setmetatable(Array, Array_mt) function Array_mt:__index(key) if type(key) ~= "string" then return nil end -- Convert underscores to camel case: num_keys -> numKeys. -- FIXME: this is pointless overhead: remove once nothing relies on it. key = underscore_to_camel_case(key) key = alias_of[key] or key local func = rawget(self, key) if func ~= nil then return func elseif (m_table_array_funcs or get_m_table_array_funcs())[key] then func = get_module_function(key, m_table or get_m_table(), "table") if (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then func = wrap_in_array_constructor(func) end elseif (m_function_array_funcs or get_m_function_array_funcs())[key] then local raw_func = get_module_function(key, m_function or get_m_function(), "fun") --[==[ Once isArray is no longer used: function func(a, b, ...) return raw_func(b, a, ...) end ]==] if key == "fold" then function func(t, f, accum) return raw_func(f, t, accum) end else function func(a, b) -- TODO: isArray parameter is probably unnecessary, and doesn't work with sparse arrays anyway. debug_track("array/isArray") return raw_func(b, a, "isArray") end end if (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then func = wrap_in_array_constructor(func) end else return nil end rawset(Array, key, func) return func end array_mt = { __index = Array, __type = "array", } function array_mt:__add(v) return setmetatable(append(self, v), array_mt or get_array_mt()) end get_array_mt = nil return array_mt end -- A function to convert string key-table modules such -- as [[Module:languages/data/2]] into arrays. -- "from" is a bad name. -- field_for_key supplies the field name in which the -- key will be stored. function export.from(map, field_for_key) local arr, i = {}, 0 for key, val in pairs(map) do i = i + 1 local new_val = shallow_copy(val) if field_for_key then new_val[field_for_key] = key end arr[i] = new_val end return setmetatable(arr, array_mt or get_array_mt()) end local export_mt = {} function export_mt:__call(...) local arr if select("#", ...) == 1 and type((...)) == "table" then arr = ... local mt = getmetatable(arr) -- If table has been loaded with mw.loadData, copy it to avoid the -- limitations of it being a virtual table. if mt and type(mt) == "table" and rawget(mt, "mw_loadData") == true then arr = shallow_copy(arr) end else arr = {...} end return setmetatable(arr, array_mt or get_array_mt()) end function export_mt:__index(key) -- Convert underscores to camel case: num_keys -> numKeys. -- FIXME: this is pointless overhead: remove once nothing relies on it. key = underscore_to_camel_case(key) key = alias_of[key] or key local func = rawget(self, key) if func ~= nil then return func elseif (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then func = get_module_function(key, m_table or get_m_table(), "table") elseif (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then func = get_module_function(key, m_function or get_m_function(), "fun") else return nil end func = wrap_in_array_constructor(func) rawset(export, key, func) return func end return setmetatable(export, export_mt) sxt52fsnry0sg9endqo2zu3aaj23mkn Module:table/numKeys 828 7992 54742 2026-09-25T21:46:17Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local math_module = "Module:math" local pairs = pairs local sort = table.sort local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end --[==[ Given a table, return an array containing all positive integer keys, sorted either in numerical order, or using a custom `keySort` function.]==] return function(t, keySort) local nums, i = {}, 0 for k in pairs(t) do if is_positive_int...' 54742 Scribunto text/plain local math_module = "Module:math" local pairs = pairs local sort = table.sort local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end --[==[ Given a table, return an array containing all positive integer keys, sorted either in numerical order, or using a custom `keySort` function.]==] return function(t, keySort) local nums, i = {}, 0 for k in pairs(t) do if is_positive_integer(k) then i = i + 1 nums[i] = k end end -- No need for [[Module:math/compare]], as the default is adequate for integers. sort(nums, keySort) return nums end 9c7afk02cuh6j5vb8qjjz77xn7dijyy Module:table/iterateList 828 7993 54743 2026-09-25T21:48:29Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local ipairs = ipairs --[==[ An iterator which works like {ipairs()}, but which only returns the value.]==] return function(t) local iter, state, k, v = ipairs(t) return function() k, v = iter(t, k) return v end end' 54743 Scribunto text/plain local ipairs = ipairs --[==[ An iterator which works like {ipairs()}, but which only returns the value.]==] return function(t) local iter, state, k, v = ipairs(t) return function() k, v = iter(t, k) return v end end kwegzdop7sk2lli5t2pvp1fgvfxyfzm Module:math 828 7994 54745 2026-09-25T21:54:40Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local byte = string.byte local ceil = math.ceil local floor = math.floor local format = string.format local is_integer -- defined below local match = string.match local select = select local tonumber = tonumber local tonumber_ext -- defined below local tostring = tostring local type = type local INF = math.huge local function sign(x, signed_0) if x > 0 then return 1 elseif x < 0 then return -1 elseif x == 0 then -- 1/(+0) is infinity...' 54745 Scribunto text/plain local export = {} local byte = string.byte local ceil = math.ceil local floor = math.floor local format = string.format local is_integer -- defined below local match = string.match local select = select local tonumber = tonumber local tonumber_ext -- defined below local tostring = tostring local type = type local INF = math.huge local function sign(x, signed_0) if x > 0 then return 1 elseif x < 0 then return -1 elseif x == 0 then -- 1/(+0) is infinity and 1/(-0) is -infinity. return signed_0 and (1 / x > 0 and 1 or -1) or 0 end -- NaN: convert to string with a forced sign prefix, and grab the first byte. local sign = byte(format("%+f", x)) return sign == 0x2B and 1 or -- + sign == 0x2D and -1 or -- - -- If there's no sign, throw an error. This shouldn't be possible, but -- avoids silent errors if it does happen. error("Internal error: cannot determine sign of " .. x) end --[==[ An extended version of {tonumber()}, which attempts to convert `x` to a number. Like {tonumber()}, it will convert from base 10 by default, and the optional parameter `base` can be used to specify a different base between 2 and 36, with the letters {A-Z} (case-insensitive) representing additional digits beyond {0-9}. When strings contain hexadecimal notation (e.g. {"0x100"}), base 16 is used as the default instead, but this is overridden if `base` is set to anything other than 16. This function differs from {tonumber()} in the following ways: * If `finite_real` is set, then the function will only return finite real numbers; inputs which would normally produce ±infinity or NaN will instead produce {nil}. * If `no_prefix` is set, then strings which start with {"0x"} will not be interpreted as containing hexadecimal notation, resulting in {nil}. * If `base` is explicitly set to {10}, then strings in hexadecimal notation will always return {nil}. This fixes a bug in {tonumber()}, which treats {base=10} the same as {base} being unset, causing base 16 to be used if `x` contains hexadecimal notation (e.g. {tonumber("0x10", 10)} returns {16}, whereas {tonumber_extended("0x10", 10)} returns {nil}).]==] function export.tonumber_extended(x, base, finite_real, no_prefix) -- TODO: tonumber() maxes out at 2^64 if the base is anything other than 10. -- TODO: support binary (0b) and octal (0o) prefixes. local n = tonumber(x, base) if not n or finite_real and (n ~= n or n == INF or n == -INF) then return nil -- If `base` is explicitly set to 10 (not simply nil), or `no_prefix` is set -- and `base` is nil or 16, filter out inputs that started with hexadecimal -- prefixes. Note that if `base` is anything else, the initial "0x" will -- have been interpreted as digits by tonumber() instead of a prefix (as "x" -- can be a digit from base 34 upwards), so there's no prefix to check for. elseif base == 10 or no_prefix and (base == nil or base == 16) then return not match(x, "^%s*[+-]?0[xX]()") and n or nil end return n end tonumber_ext = export.tonumber_extended --[==[ Converts `x` to an integer by removing the fractional portion (e.g. {3.5} becomes {3}, and {-2.9} becomes {-2}). This is equivalent to rounding down positive numbers and rounding up negative numbers. If conversion is not possible, returns {nil}.]==] function export.to_integer(x) x = tonumber(x) if not (x and x == x and x ~= INF and x ~= -INF) then return nil elseif x % 1 == 0 then return x -- Round-down positives. elseif x >= 0 then return floor(x) end --Round-up negatives. return ceil(x) end --[==[ Returns {1} if `x` is positive, {-1} if `x` is negative, or {0} if `x` is {0}. If `signed_0` is set, this function will only return either {1} or {-1}, and will make a distinction between [[w:signed zero|signed zeroes]] ({+0} and {-0}). This is useful when a {0} result could be disruptive (e.g. {x % 0}).]==] function export.sign(x, signed_0) return sign( tonumber(x) or error(format("bad argument #1 to 'sign' (number expected, got %s)", type(x)), 2), signed_0 ) end --[==[ Returns {true} if `x` is a finite real number, or {false} if not.]==] function export.is_finite_real_number(x) return x and x == x and not (x == INF or x == -INF) and type(x) == "number" end --[==[ Returns {true} if `x` is an integer, or {false} if not.]==] function export.is_integer(x) return x and type(x) == "number" and x % 1 == 0 or false end is_integer = export.is_integer --[==[ Returns {true} if `x` is a positive integer (or zero if the `include_0` flag is set), or {false} if not.]==] function export.is_positive_integer(x, include_0) return x and type(x) == "number" and (x > 0 or include_0 and x == 0) and x % 1 == 0 or false end --[==[ Returns {true} is `x` is [[w:NaN|NaN]] (Not a Number), or {false} if not. NaN is a value that has the type "number", but does not represent an actual numeric value; it has the unique property that if {x} is NaN, {x ~= x} evaluates to {true}.]==] function export.is_NaN(x) return x ~= x end --[==[ Returns the base-10 logarithm of `x`. This function should be used instead of {math.log10}, which is deprecated and may stop working if Scribunto is updated to a more recent Lua version.]==] function export.log10(x) -- Structured like this so that module documentation works. local log10 = math.log10 if log10 ~= nil then return log10 end local log = math.log return log(10, 10) == 1 and function(x) -- Lua 5.2 return log(x, 10) end or function(x) -- Lua 5.1 return log(x) * 0.43429448190325182765112891891660508229439700580367 -- log10(e) end end export.log10 = export.log10() -- Sets the actual returned function. local function integer_error(x, param, func_name) local type_x = type(x) error(format( "bad argument #%d to '%s' (integer expected, got %s)", param, func_name, type_x == "number" and tostring(x) or type_x ), 3) end --[==[ Converts a decimal number to hexadecimal. If `include_prefix` is set, the returned number will include the 0x prefix.]==] function export.to_hex(dec, include_prefix) dec = tonumber(dec) or dec if not is_integer(dec) then integer_error(dec, 1, "to_hex") end local neg = dec < 0 if neg then dec = -dec end -- Inputs >= 2^64 cause string.format to return "0". if dec >= 0x1p64 then error("integer overflow in 'to_hex': cannot convert inputs with a magnitude greater than or equal to 2^64 (18446744073709551616)", 2) end -- string.format treats hex numbers as unsigned, so any sign must be added manually. return format("%s%s%X", neg and "-" or "", include_prefix and "0x" or "", dec) end --[==[ Returns the greatest common divisor of an arbitrary number of input numbers.]==] function export.gcd(x, ...) x = tonumber(x) or x if not is_integer(x) then integer_error(x, 1, "gcd") end local q, args_len, integers = ..., select("#", ...) -- Compute p_1 = gcd(n_1, n_2), p_2 = gcd(p_1, n_3), ... i.e. compute GCD by Euclid's algorithm for the current result and the next number. for i = 2, args_len + 1 do q = tonumber(q) or q if not is_integer(q) then integer_error(q, i, "gcd") elseif x ~= 1 then -- If x is 1, validate remaining inputs. -- GCD of two integers x, q with Euclid's algorithm. while q ~= 0 do x, q = q, x % q end end if i <= args_len then -- Only create a table if absolutely necessary, as it's inefficient. if i == 2 then integers = {...} end q = integers[i] end end return x < 0 and -x or x end --[==[ Returns the least common multiple of an arbitrary number of input numbers.]==] function export.lcm(x, ...) x = tonumber(x) or x if not is_integer(x) then integer_error(x, 1, "lcm") end local q, args_len, integers = ..., select("#", ...) -- Compute the product of all inputs as p and GCD as x. for i = 2, args_len + 1 do q = tonumber(q) or q if not is_integer(q) then integer_error(q, i, "lcm") elseif x ~= 0 then -- If x is 0, validate remaining inputs. -- Compute the product. local p = x * q -- GCD of two integers x, q with Euclid's algorithm. while q ~= 0 do x, q = q, x % q end -- Divide product by the GCD to get new LCM. x = p / x end if i <= args_len then -- Only create a table if absolutely necessary, as it's inefficient. if i == 2 then integers = {...} end q = integers[i] end end return x < 0 and -x or x end return export 9iueib95u6jd0lc3zzrya3gsltprarl Module:table/shallowCopy 828 7995 54746 2026-09-25T21:58:03Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local next = next local pairs = pairs local type = type --[==[ Returns a clone of an object. If the object is a table, the value returned is a new table, but all subtables and functions are shared. Metamethods are respected unless the `raw` flag is set, but the returned table will have no metatable of its own.]==] return function(orig, raw) if type(orig) ~= "table" then return orig end local copy, iter, state, init = {} if raw then iter, state = next, or...' 54746 Scribunto text/plain local next = next local pairs = pairs local type = type --[==[ Returns a clone of an object. If the object is a table, the value returned is a new table, but all subtables and functions are shared. Metamethods are respected unless the `raw` flag is set, but the returned table will have no metatable of its own.]==] return function(orig, raw) if type(orig) ~= "table" then return orig end local copy, iter, state, init = {} if raw then iter, state = next, orig else iter, state, init = pairs(orig) end for k, v in iter, state, init do copy[k] = v end return copy end t51fcj6fuqkknha7dzzm33l4y4ohb22 Module:language-like 828 7996 54747 2026-09-25T21:58:38Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local families_module = "Module:families" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" -- In a locally installed Docker container containing Wiktionary, Wikibase is typically not installed because it's -- a real pain to get working properly. In such a case, `mw.wikibase` returns nil. We now have checks in the code -- below so it does reasonable things in the absence of Wikibase. local wikibase =...' 54747 Scribunto text/plain local export = {} local families_module = "Module:families" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" -- In a locally installed Docker container containing Wiktionary, Wikibase is typically not installed because it's -- a real pain to get working properly. In such a case, `mw.wikibase` returns nil. We now have checks in the code -- below so it does reasonable things in the absence of Wikibase. local wikibase = mw.wikibase local category_name_has_suffix -- defined as export.categoryNameHasSuffix below local get_entity = wikibase and wikibase.getEntity or nil local get_entity_id_for_title = wikibase and wikibase.getEntityIdForTitle or nil local gsub = string.gsub local ipairs = ipairs local match = string.match local select = select local sitelink = wikibase and wikibase.sitelink or nil local type = type local umatch = mw.ustring.match --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function case_insensitive_pattern(...) case_insensitive_pattern = require(string_utilities_module).case_insensitive_pattern return case_insensitive_pattern(...) end local function table_flatten(...) table_flatten = require(table_module).flatten return table_flatten(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local content_lang local function get_content_lang() content_lang, get_content_lang = mw.getContentLanguage(), nil return content_lang end -- Implementation of getAliases() for languages, etymology languages, -- families, scripts and writing systems. function export.getAliases(self) local aliases = self._aliases if aliases == nil then aliases = (self._data or self).aliases or {} self._aliases = aliases end return aliases end -- Implementation of getVarieties() for languages, etymology languages, -- families, scripts and writing systems. If `flatten` is passed in, -- flatten down to a list of strings; otherwise, keep the structure. function export.getVarieties(self, flatten) local varieties = self._varieties if varieties == nil then varieties = (self._data or self).varieties or {} self._varieties = varieties end if not flatten then return varieties end local flattened_varieties = self._flattened_varieties if flattened_varieties == nil then flattened_varieties = table_flatten(varieties) self._flattened_varieties = flattened_varieties end return flattened_varieties end -- Implementation of getOtherNames() for languages, etymology languages, -- families, scripts and writing systems. function export.getOtherNames(self) local other_names = self._other_names if other_names == nil then other_names = (self._data or self).other_names or {} self._other_names = other_names end return other_names end -- Implementation of getAllNames() for languages, etymology languages, -- families, scripts and writing systems. function export.getAllNames(self) local all_names = self._allNames if all_names == nil then all_names = table_flatten{ self.getCanonicalNames and self:getCanonicalNames() or self:getCanonicalName(), self:getAliases(), self:getVarieties(), self:getOtherNames(), } self._allNames = all_names end return all_names end function export.hasType(self, ...) local n = select("#", ...) if n == 0 then error("Must specify at least one type.") end local types = self:getTypes() if not types[...] then return false elseif n == 1 then return true end local args = {...} for i = 2, n do if not types[args[i]] then return false end end return true end -- Implementation of template-callable getByCode() function for languages, -- etymology languages, families and scripts. `item` is the language, -- family or script in question; `args` is the arguments passed in by the -- module invocation; `extra_processing`, if specified, is a function of -- one argument (the requested property) and should return the value to -- be returned to the caller, or nil if the property isn't recognized. -- `extra_processing` is called after special-cased properties are handled -- and before general-purpose processing code that works for all string -- properties. function export.templateGetByCode(args, extra_processing) -- The item that the caller wanted to look up. local item, itemname, list = args[1], args[2] if itemname == "getAllNames" then list = item:getAllNames() elseif itemname == "getOtherNames" then list = item:getOtherNames() elseif itemname == "getAliases" then list = item:getAliases() elseif itemname == "getVarieties" then list = item:getVarieties(true) end if list then local index = args[3]; if index == "" then index = nil end index = tonumber(index or error("Numeric index of the desired item in the list (parameter 3) has not been specified.")) return list[index] or "" end if itemname == "getFamily" and item.getFamily then return item:getFamily():getCode() end if extra_processing then local retval = extra_processing(itemname) if retval then return retval end end if item[itemname] then local ret = item[itemname](item) if type(ret) == "string" then return ret end error("The function \"" .. itemname .. "\" did not return a string value.") end error("Requested invalid item name \"" .. itemname .. "\".") end -- Implementation of getCommonsCategory() for languages, etymology languages, -- families, scripts and writing systems. function export.getWikidataItem(self) local item = self._WikidataItem if item == nil then item = (self._data or self)[2] -- If the value is nil, it's cached as false. item = item ~= nil and (type(item) == "number" and "Q" .. item or item) or false self._WikidataItem = item end return item or nil end do local function get_wiki_article(self, project) local article -- If the project is enwiki, check the language data. if project == "enwiki" then article = (self._data or self).wikipedia_article if article then return article end end -- Otherwise, check the Wikidata item for a sitelink. local item = self:getWikidataItem() article = item and sitelink and sitelink(item, project) or false if article then return article end -- If there's still no article, try the parent (if any). local get_parent = self.getParent if get_parent then local parent = get_parent(self) if parent then return get_wiki_article(parent, project) end end return false end -- Implementation of getWikipediaArticle() for languages, etymology languages, -- families, scripts and writing systems. function export.getWikipediaArticle(self, noCategoryFallback, project) if project == nil then project = "enwiki" end local article if project == "enwiki" then article = self._wikipedia_article if article == nil then article = get_wiki_article(self, project) self._wikipedia_article = article end else -- If the project isn't enwiki, default to no category fallback, but -- this can be overridden by specifying the value `false`. if noCategoryFallback == nil then noCategoryFallback = true end local non_en_wikipedia_articles = self._non_en_wikipedia_articles if non_en_wikipedia_articles == nil then non_en_wikipedia_articles = {} self._non_en_wikipedia_articles = non_en_wikipedia_articles else article = non_en_wikipedia_articles[project] end if article == nil then article = get_wiki_article(self, project) non_en_wikipedia_articles[project] = article end end if article or noCategoryFallback then return article or nil end return (gsub(self:getCategoryName(), "Creole language", "Creole")) end end do local function get_commons_cat_claim(item) if item then local entity = get_entity and get_entity(item) or nil if entity then -- P373 is the "Commons category" property. local claim = entity:getBestStatements("P373")[1] return claim and ("Category:" .. claim.mainsnak.datavalue.value) or nil end end end local function get_commons_cat_sitelink(item) if item then local commons_sitelink = sitelink and sitelink(item, "commonswiki") or nil -- Reject any sitelinks that aren't categories. return commons_sitelink and match(commons_sitelink, "^Category:") and commons_sitelink or nil end end local function get_commons_cat(self) -- Checks are in decreasing order of likelihood for a useful match. -- Get the Commons Category claim from the object's item. local lang_item = self:getWikidataItem() local category = get_commons_cat_claim(lang_item) if category then return category end -- Otherwise, try the object's category's item. local langcat_item = get_entity_id_for_title and get_entity_id_for_title("Category:" .. self:getCategoryName()) or nil category = get_commons_cat_claim(langcat_item) if category then return category end -- If there's no P373 claim, there might be a sitelink on the -- object's category's item. category = get_commons_cat_sitelink(langcat_item) if category then return category end -- Otherwise, try for a sitelink on the object's own item. category = get_commons_cat_sitelink(lang_item) if category then return category end -- If there's still no category, try the parent (if any). local get_parent = self.getParent if get_parent then local parent = get_parent(self) if parent then return get_commons_cat(parent) end end return false end -- Implementation of getCommonsCategory() for languages, etymology -- languages, families, scripts and writing systems. function export.getCommonsCategory(self) local category category = self._commons_category -- Nil values cached as false. if category ~= nil then return category or nil end category = get_commons_cat(self) self._commons_category = category return category or nil end end function export.categoryNameHasSuffix(name, suffixes) for _, suffix in ipairs(suffixes) do if umatch(name, "%f[%w]" .. case_insensitive_pattern(suffix, "^.") .. "$") then return false end end return true end category_name_has_suffix = export.categoryNameHasSuffix function export.categoryNameToCode(name, suffix, data, suffixes) local truncated = match(name, "(.*)" .. suffix .. "$") if truncated and category_name_has_suffix(truncated, suffixes) then local code = data[truncated] if code ~= nil then return code, truncated end truncated = (content_lang or get_content_lang()):lcfirst(truncated) code = data[truncated] if code ~= nil then return code, truncated end end if not category_name_has_suffix(name, suffixes) then local code = data[name] if code ~= nil then return code, name end name = (content_lang or get_content_lang()):lcfirst(name) code = data[name] if code ~= nil then return code, name end end return nil, nil end -- If a language has no specified pseudo-family but its or its family's code begins with one or these prefixes, -- assign it to the specified pseudo-family. Note that we don't do this across-the-board because there are (currently) -- some families whose code begins with `nai-` but are considered in the `cai` pseudo-family. local pseudo_family_prefixes = { cai = true, nai = true, paa = true, sai = true, sgn = true, } -- Implementation of getPseudoFamilies() for languages, etymology languages -- and families. function export.getPseudoFamilies(self) local pseudo_families = self._pseudo_families local function insert_pseudo_family(code) local fam = require(families_module).getByCode(code) if not fam then mw.log(("WARNING: Unrecognized pseudo-family code '%s'"):format(code)) else table.insert(pseudo_families, fam) end end if pseudo_families == nil then pseudo_families = {} local pseudo_family_string = (self._data or self).pseudo_families if pseudo_family_string then local pseudo_family_codes = require(string_utilities_module).split(pseudo_family_string, "%s*,%s*") for _, code in ipairs(pseudo_family_codes) do insert_pseudo_family(code) end end if not pseudo_families[1] then -- Check the parent. If it's a full language, we will have to fetch the extra data to get the -- list of pseudo-families. local get_parent = self.getParent if get_parent then local parent = get_parent(self) if parent then local parent_code = parent:getCode() if pseudo_family_prefixes[parent_code] then insert_pseudo_family(parent_code) else pseudo_families = parent:getPseudoFamilies() end end end end if not pseudo_families[1] then -- Check the family to see if it's in any pseudo-families. local get_family = self.getFamily if get_family then local family = get_family(self) if family then local family_code = family:getCode() if pseudo_family_prefixes[family_code] then insert_pseudo_family(family_code) elseif family_code ~= self:getCode() then -- qfa-not has itself as a family. pseudo_families = family:getPseudoFamilies() end end end end if not pseudo_families[1] then local prefix = self:getCode():match("^([a-z][a-z][a-z])%-") if prefix and pseudo_family_prefixes[prefix] then insert_pseudo_family(prefix) end end self._pseudo_families = pseudo_families end return pseudo_families end return export sc8z3bbnk6oc9a4zd42q6we7t7b9pla Module:table/listToSet 828 7997 54748 2026-09-25T21:59:40Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local fun_is_callable_module = "Module:fun/isCallable" local function is_callable(...) is_callable = require(fun_is_callable_module) return is_callable(...) end --[==[ Convert `list` (a table with a list of values) into a set (a table where those values are keys instead). This is a useful way to create a fast lookup table, since looking up a table key is much, much faster than iterating over the whole list to see if it contains a given value. By default, eac...' 54748 Scribunto text/plain local fun_is_callable_module = "Module:fun/isCallable" local function is_callable(...) is_callable = require(fun_is_callable_module) return is_callable(...) end --[==[ Convert `list` (a table with a list of values) into a set (a table where those values are keys instead). This is a useful way to create a fast lookup table, since looking up a table key is much, much faster than iterating over the whole list to see if it contains a given value. By default, each item is given the value true. If the optional parameter `value` is a function or functor, then it is called as an iterator, with the list index as the first argument, the item as the second (which will be used as the key), plus any additional arguments passed to {listToSet}; the returned value is used as the value for that list item. If `value` is anything else, then it is used as the fixed value for every item.]==] return function(list, value, ...) local set, i = {}, 1 if value == nil then value = true elseif is_callable(value) then while true do local item = list[i] if item == nil then return set end set[item] = value(i, item, ...) i = i + 1 end end while true do local item = list[i] if item == nil then return set end set[item] = value i = i + 1 end end o2d2ojf8rsrqqaqy0ffn7n4y1a1r9tb Module:fun/isCallable 828 7998 54749 2026-09-25T22:00:08Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local debug_track_module = "Module:debug/track" local table_get_metamethod_module = "Module:table/getMetamethod" local require = require local type = type local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end --[==[ Return {true} if the input is a function or functor (an object which can be...' 54749 Scribunto text/plain local debug_track_module = "Module:debug/track" local table_get_metamethod_module = "Module:table/getMetamethod" local require = require local type = type local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end --[==[ Return {true} if the input is a function or functor (an object which can be called like a function, because it has a {__call} metamethod). Note: if the input is an object with a {__call} metamethod, but this function is not able to find it because the object's metatable is protected with {__metatable}, then it will return {false} by default, or {nil} if the {allow_maybe} flag is set.]==] return function(obj, allow_maybe) if type(obj) == "function" then return true end -- An object is callable if it has a __call metamethod, so try to get it -- with get_metamethod(). local success, __call = get_metamethod(obj, "__call") -- If this succeeds, `obj` will only be callable if the __call metamethod is -- a function (i.e. it can't itself be a callable table), so don't recurse -- to check it. if __call and type(__call) == "function" then return true -- If not, then the metatable is protected, so it's not possible to know if -- `obj` is callable without actually calling it. elseif not success then debug_track("fun/isCallable/protected metatable") if allow_maybe then return nil end end return false end nr6fmojt64uvmsick8bst0i74ec4d2z Module:table/getMetamethod 828 7999 54750 2026-09-25T22:01:27Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local rawget = rawget local select = select local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local function get_metamethod(n, i, mt, mm, ...) if mt then mm = rawget(mt, mm) else mm = nil end if i == n then return mm end return mm, get_metamethod(n, i + 1, mt, ......' 54750 Scribunto text/plain local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local rawget = rawget local select = select local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local function get_metamethod(n, i, mt, mm, ...) if mt then mm = rawget(mt, mm) else mm = nil end if i == n then return mm end return mm, get_metamethod(n, i + 1, mt, ...) end --[==[ Takes an input value and any number of metamethods, and attempts to retrieve the values at those keys in the value's metatable. Returns a boolean stating whether the value's metamethods are findable, followed by a return value for each metamethod. A value's metamethods are findable if it does not have a protected metatable, so the first return value is {true} if the value either has a metatable which does not contain the {__metatable} metamethod, or it has no metatable at all. If the value does have a protected metatable, the first return value is {false}, and all remaining return values default to {nil}.]==] return function(t, ...) local mt, n = get_unprotected_metatable(t), select("#", ...) -- get_unprotected_metatable() returns the metatable if it's unprotected, -- false if it's protected, or nil if there is no metatable. It checks -- whether a metatable is protected by trying pcall(setmetatable, t), -- so it's always possible to determine accurately. if n == 0 then return mt ~= false end return mt ~= false, get_metamethod(n, 1, mt, ...) end tfl070cq0h5icostaevq3t6kcrhpcyw Module:table/getUnprotectedMetatable 828 8000 54751 2026-09-25T22:01:50Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local _getmetatable = debug.getmetatable -- For testing (and just in case it gets enabled). if _getmetatable ~= nil then -- Avoid debug.getmetatable() throwing an error if 0 arguments are passed, -- for parity with the other function. return function(t) return _getmetatable(t) end end _getmetatable = getmetatable local pcall = pcall local rawget = rawget local setmetatable = setmetatable local type = type --[==[ Attempts to retrieve the input value's me...' 54751 Scribunto text/plain local _getmetatable = debug.getmetatable -- For testing (and just in case it gets enabled). if _getmetatable ~= nil then -- Avoid debug.getmetatable() throwing an error if 0 arguments are passed, -- for parity with the other function. return function(t) return _getmetatable(t) end end _getmetatable = getmetatable local pcall = pcall local rawget = rawget local setmetatable = setmetatable local type = type --[==[ Attempts to retrieve the input value's metatable, and returns it if found. If the value does not have a metatable, returns {nil}. If the input value does have a metatable, but that metatable is not possible to retrieve because it is protected with the `__metatable` metamethod, returns {false}. This is a useful way to ensure that functions can reliably distinguish between objects that do not have metamethods, objects with known metamethods, and objects with unknown metamethods.]==] return function(t) local mt = _getmetatable(t) -- If `mt` is nil, there's no metatable. if mt == nil then return nil -- If `mt` is not a table, the real metatable is protected and there's no -- way of retrieving it. elseif type(mt) ~= "table" then return false end -- Try setting `mt` as the metatable with `setmetatable`; if the metatable -- is protected, this will cause an error to be thrown (revealing it as -- protected), and if it isn't, then `mt` must be the real metatable anyway, -- so nothing has changed. Also make a special exception for data loaded via -- mw.loadData(), which sets each metatable at its own __metatable key as a -- way to stop the use of setmetatable() without actually hiding it. This is -- spoofable, but low-risk. return (pcall(setmetatable, t, mt) or rawget(mt, "mw_loadData") == true) and mt or false end oqd72lzzcljldqpmbzxsx5fm947exar Module:table/setNested 828 8001 54752 2026-09-25T22:02:17Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local error = error local select = select --[==[ Given a table, value and an arbitrary number of keys, will successively access subtables using each key in turn, and sets the value at the final key. For example, if {t} is { {} }, {export.setNested(t, "foo", 1, 2, 3)} will modify {t} to { {[1] = {[2] = {[3] = "foo"} } } }. If no subtable exists for a given key value, one will be created, but the function will throw an error if a non-table value is found at an in...' 54752 Scribunto text/plain local error = error local select = select --[==[ Given a table, value and an arbitrary number of keys, will successively access subtables using each key in turn, and sets the value at the final key. For example, if {t} is { {} }, {export.setNested(t, "foo", 1, 2, 3)} will modify {t} to { {[1] = {[2] = {[3] = "foo"} } } }. If no subtable exists for a given key value, one will be created, but the function will throw an error if a non-table value is found at an intermediary key. Note: the parameter order (table, value, keys) differs from functions like rawset, because the number of keys can be arbitrary. This is to avoid situations where an additional argument must be appended to arbitrary lists of variables, which can be awkward and error-prone: for example, when handling variable arguments ({...}) or function return values.]==] return function(...) local n = select("#", ...) if n < 3 then error("Must provide a table, value and at least one key.") end local t, v, k = ... for i = 4, n do local next_t = t[k] if next_t == nil then -- If there's no next table while setting nil, there's nothing more -- to do. if v == nil then return end next_t = {} t[k] = next_t end t, k = next_t, select(i, ...) end t[k] = v end 1jxfjipchypy8qfjkmtb9b9sal1bcc1 Module:table/deepCopy 828 8002 54753 2026-09-25T22:03:25Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local debug_track_module = "Module:debug/track" local dump = mw.dumpObject local error = error local getmetatable = getmetatable local next = next local pairs = pairs local rawget = rawget local type = type local setmetatable = setmetatable local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local tracked local function make_copy(orig, seen, mt_flag, keep_loaded_data) local mt, iter, state, init = getmetata...' 54753 Scribunto text/plain local debug_track_module = "Module:debug/track" local dump = mw.dumpObject local error = error local getmetatable = getmetatable local next = next local pairs = pairs local rawget = rawget local type = type local setmetatable = setmetatable local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local tracked local function make_copy(orig, seen, mt_flag, keep_loaded_data) local mt, iter, state, init = getmetatable(orig) -- If `mt` is nil, just use `next`, as it's faster. if mt == nil then iter, state, init = next, orig, nil -- `mt` could be a non-table if `__metatable` has been used, but discard it in such cases. elseif type(mt) ~= "table" then mt, iter, state, init = nil, pairs(orig) -- Data loaded via `mw.loadData`, which sets the key "mw_loadData" to true in the metatable. elseif rawget(mt, "mw_loadData") == true then if keep_loaded_data then seen[orig] = orig return orig -- Track instances of such data being copied, which is very inefficient and usually unnecessary. elseif not tracked then debug_track("table/deepCopy/loaded data") tracked = true end -- Discard the metatable and use the `__pairs` metamethod. mt, iter, state, init = nil, pairs(orig) -- If `mt_flag` is "none", discard the metatable and use the `__pairs` metamethod. elseif mt_flag == "none" then mt, iter, state, init = nil, pairs(orig) -- Otherwise, keep `mt` and use `next` to copy the raw contents. else iter, state, init = next, orig, nil end local copy = {} seen[orig] = copy for k, v in iter, state, init do if k and type(k) == "table" then k = seen[k] or make_copy(k, seen, mt_flag, keep_loaded_data) end if v and type(v) == "table" then v = seen[v] or make_copy(v, seen, mt_flag, keep_loaded_data) end copy[k] = v end if mt == nil or mt_flag == "none" then return copy -- Copy the metatable if `mt_flag` is "copy"; otherwise, it will be "keep", so keep it. elseif mt_flag == "copy" then mt = seen[mt] or make_copy(mt, seen, mt_flag, keep_loaded_data) end return setmetatable(copy, mt) end --[==[ Recursive deep copy function. Preserves copied identities of subtables. A more powerful version of {mw.clone}, with customizable options. * `metatableFlag` can be one of three options: *# "none" (the default): `pairs` will be used to copy the table, meaning that any `__pairs` metamethod will be used to copy the table, if available; the resulting table will not have a metatable. *# "copy": a raw copy of the table will be made (i.e. any `__pairs` metamethod will be ignored), and the copy will be given a copy of the original table's metatable; this ensures that nothing from the original is retained, but may cause metamethods to behave unexpectedly, depending on their implementation. *# "keep": a raw copy of the table will be made (i.e. any `__pairs` metamethod will be ignored), and the copy will be given the original table's metatable; this is useful when copying objects that inherit methods from a prototype object (e.g. language objects). * If `keepLoadedData` is true, then any data loaded via {mw.loadData} will not be copied, and the original will be used instead. This is useful in iterative contexts where it is necessary to copy data being destructively modified, because objects loaded via mw.loadData are immutable. * Notes: *# Protected metatables will not be copied (i.e. those hidden behind a `__metatable` metamethod), as they are not accessible by Lua's design. Instead, the output of the `__metatable` method will be used instead. *# Data loaded via {mw.loadData} is always treated as though the "none" flag is set, because the way it has been implemented causes errors to be thrown if "copy" or "keep" are used with it.]==] return function(orig, metatableFlag, keepLoadedData) if metatableFlag == nil then metatableFlag = "none" elseif not (metatableFlag == "keep" or metatableFlag == "copy" or metatableFlag == "none") then error('metatableFlag must be "none", "copy", "keep" or nil; received ' .. dump(metatableFlag)) end if orig and type(orig) == "table" then return make_copy(orig, {}, metatableFlag, keepLoadedData) end return orig end n283xd4cxzrnpyf4zrlt9334gjwqw59 Module:table/getNested 828 8003 54754 2026-09-25T22:04:00Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local error = error local select = select --[==[ Given a table and an arbitrary number of keys, will successively access subtables using each key in turn, returning the value at the final key. For example, if {t} is { {[1] = {[2] = {[3] = "foo"}}}}, {export.getNested(t, 1, 2, 3)} will return {"foo"}. If no subtable exists for a given key value, returns nil, but will throw an error if a non-table is found at an intermediary key.]==] return function(...) local n...' 54754 Scribunto text/plain local error = error local select = select --[==[ Given a table and an arbitrary number of keys, will successively access subtables using each key in turn, returning the value at the final key. For example, if {t} is { {[1] = {[2] = {[3] = "foo"}}}}, {export.getNested(t, 1, 2, 3)} will return {"foo"}. If no subtable exists for a given key value, returns nil, but will throw an error if a non-table is found at an intermediary key.]==] return function(...) local n = select("#", ...) if n < 2 then error("Must provide a table and at least one key.") end local t, k = ... for i = 3, n do local v = t[k] if v == nil then return nil end t, k = v, select(i, ...) end return t[k] end tm1q0pycz7xr0ute9n4h4yhpf1jzo33 Module:title/newTitle 828 8004 54755 2026-09-25T22:04:43Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local load_module = "Module:load" local scribunto_module = "Module:Scribunto" local title_get_current_namespace_module = "Module:title/getCurrentNamespace" local title_get_current_title_module = "Module:title/getCurrentTitle" local title_get_main_page_title_module = "Module:title/getMainPageTitle" local byte = string.byte local find = string.find local match = string.match local new_title = mw.title.new local reverse = string.reverse local sub = string.sub local...' 54755 Scribunto text/plain local load_module = "Module:load" local scribunto_module = "Module:Scribunto" local title_get_current_namespace_module = "Module:title/getCurrentNamespace" local title_get_current_title_module = "Module:title/getCurrentTitle" local title_get_main_page_title_module = "Module:title/getMainPageTitle" local byte = string.byte local find = string.find local match = string.match local new_title = mw.title.new local reverse = string.reverse local sub = string.sub local type = type local function get_current_title(...) get_current_title = require(title_get_current_title_module) return get_current_title(...) end local function get_main_page_title(...) get_main_page_title = require(title_get_main_page_title_module) return get_main_page_title(...) end local function php_ltrim(...) php_ltrim = require(scribunto_module).php_ltrim return php_ltrim(...) end local function php_rtrim(...) php_rtrim = require(scribunto_module).php_rtrim return php_rtrim(...) end local current_pagename local function get_current_pagename() -- Call mw.title.getCurrentTitle() directly rather than the modified version -- at [[Module:title/getCurrentTitle]], as it doesn't do anything extra when -- no fragment argument is given, so there's no point in loading it. current_pagename, get_current_pagename = mw.title.getCurrentTitle().prefixedText, nil return current_pagename end local namespace_has_subpages local function get_namespace_has_subpages() namespace_has_subpages, get_namespace_has_subpages = require(load_module).load_data(title_get_current_namespace_module).hasSubpages, nil return namespace_has_subpages end local function split_text(text) -- Split off the fragment. local hash, target, fragment = find(text, "#", nil, true) if hash then target, fragment = sub(text, 1, hash - 1), sub(text, hash) else target, fragment = text, "" end return php_rtrim(target), fragment end --[==[ A modified version of {mw.title.new}: * It is no longer possible to generate title objects for the empty string by inputting a title starting with {"#"}. Such empty string titles do not represent a valid page, and are broken in various ways (e.g. attempting to access certain keys results in an error); see [[phab:T240678]]. * There are two additional boolean flags: ** If {allowOnlyFragment} is set, string inputs starting with {#} are handled analogously to links (e.g. the input {"#foo"} returns a title object for the current page with the addition of the fragment {"foo"}, analogous to the link [[#foo]]). As a special case, the input {"#"} returns the title for the main page (see TitleValue.php). ** If {allowRelative} is set, inputs representing relative titles will work (e.g. {"/foo"} and {"../"}, analogous to the relative links [[/foo]] and [[../]]).]==] return function(text_or_id, defaultNamespace, allowOnlyFragment, allowRelative) -- Process relative titles in the same way as normalizeSubpageLink() in -- Linker.php. if ( allowRelative and type(text_or_id) == "string" and -- Distinguish nil and false for the purposes of calling the getter. (namespace_has_subpages == nil and get_namespace_has_subpages() or namespace_has_subpages) ) then -- For both kinds of relative title, `text_or_id` has to be split into -- `target` and `fragment` (if any). `target` has to be trimmed, but -- as an optimisation, only do a left-trim on `text_or_id`, which is -- sufficient for the initial checks for potential relative titles, as -- this avoids unnecessary splitting/trimming in the vast majority of -- cases where the title won't be relative. In the cases where it is, -- `target` will then be right-trimmed once it has been split out of -- `text_or_id`. text_or_id = php_ltrim(text_or_id) local init = byte(text_or_id) -- If the target starts with "/", it's treated as a subpage of the -- current page. Final slashes are trimmed, but this can't affect the -- intervening slash (e.g. "[[///]]" refers to "[[{{PAGENAME}}/]]"). if init == 0x2F then -- / local target, fragment = split_text(text_or_id) text_or_id, defaultNamespace = (current_pagename or get_current_pagename()) .. (match(target, "^/.*[^/]") or "/") .. fragment, nil -- If the title starts with "../", trim it and any further "../" that -- follow, and go up that many subpage levels. Then, treat any -- additional text as a subpage of that page. Final slashes are trimmed. elseif init == 0x2E and sub(text_or_id, 2, 3) == "./" then -- ../ local n, target, fragment = 4, split_text(text_or_id) while sub(target, n, n + 2) == "../" do n = n + 3 end -- Retain an initial "/". target = sub(target, n - 1) -- Trim the relevant number of subpages from the pagename. local pagename, i = reverse(current_pagename or get_current_pagename()), 0 for _ = 1, (n - 1) / 3 do i = find(pagename, "/", i + 1, true) -- Fail if there aren't enough slashes. if not i then return nil end end -- Add the subpage text; since the intervening "/" is retained in -- `target`, it can be trimmed along with any other final slashes -- (e.g. [[..///]] refers to "{{BASEPAGENAME}}".) text_or_id, defaultNamespace = reverse(sub(pagename, i + 1)) .. (match(target, "^.*[^/]") or "") .. fragment, nil end end local title = new_title(text_or_id, defaultNamespace) if not title then return nil elseif title.prefixedText ~= "" then return title -- If `prefixedText` is the empty string, the input only has a fragment. elseif not allowOnlyFragment then return nil end -- Retrieve the fragment: if it's the empty string, return a title object -- for the main page (which is where the link [[#]] resolves); otherwise, -- return a current title object using [[Module:title/getCurrentTitle]], -- which sets the fragment on the returned title to the string passed in. -- (Note that inputs like "##foo" aren't interpreted as the main page with -- the fragment "foo"; instead, it's a fragment for the section "#foo" on -- the current page, since "#" is itself a valid fragment character.) local fragment = title.fragment return fragment == "" and get_main_page_title() or get_current_title(fragment) end aooqpfbxewgzf96rcljwg1e7vf0j43b Module:data/magic words 828 8005 54756 2026-09-25T22:05:36Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local next = next local date_and_time = "mw:Help:Magic words#Date and time" local escaped_characters = "mw:Help:Magic words#Escaped characters" local formatting = "mw:Help:Magic words#Formatting" local labeled_section_transclusion = "mw:Extension:Labeled Section Transclusion" local liquidthreads = "mw:Extension:LiquidThreads" local localization_functions = "mw:Help:Magic words#Localization functions" local localization_variables = "mw:Help:Magic words#Localizati...' 54756 Scribunto text/plain local next = next local date_and_time = "mw:Help:Magic words#Date and time" local escaped_characters = "mw:Help:Magic words#Escaped characters" local formatting = "mw:Help:Magic words#Formatting" local labeled_section_transclusion = "mw:Extension:Labeled Section Transclusion" local liquidthreads = "mw:Extension:LiquidThreads" local localization_functions = "mw:Help:Magic words#Localization functions" local localization_variables = "mw:Help:Magic words#Localization variables" local miscellaneous = "mw:Help:Magic words#Miscellaneous" local namespaces = "mw:Help:Magic words#Namespaces" local namespaces_2 = "mw:Help:Magic words#Namespaces 2" local noexternallanglinks = "mw:Wikibase/Installation/Advanced configuration#noexternallanglinks" local page_names = "mw:Help:Magic words#Page names" local parser_functions_ext = "mw:Help:Extension:ParserFunctions##" -- ## is not a typo local statistics = "mw:Help:Magic words#Statistics" local substitution = "mw:Manual:Substitution" local technical_metadata = "mw:Help:Magic words#Technical metadata" local technical_metadata_of_another_page = "mw:Help:Magic words#Technical metadata of another page" local transclusion_modifiers = "mw:Help:Magic words#Transclusion modifiers" local url_data = "mw:Help:Magic words#URL data" local data = {} for k, v in next, { ["!"] = { parser_variable = escaped_characters, case_sensitive = false }, ["#BABEL"] = { parser_function = "mw:Extension:Babel#Usage", case_sensitive = false }, ["#bcp47"] = { -- Case-sensitive lowercase. parser_function = localization_functions, parser_variable = localization_functions, -- Not a typo. case_sensitive = true }, ["#CATEGORYTREE"] = { parser_function = "mw:Extension:CategoryTree#The {{#categorytree}} parser function", case_sensitive = false }, ["#COMMASEPARATEDLIST"] = { parser_function = formatting, case_sensitive = false }, ["#dir"] = { -- Case-sensitive lowercase. parser_function = localization_functions, parser_variable = localization_functions, -- Not a typo. case_sensitive = true }, ["#EXPR"] = { parser_function = parser_functions_ext .. "expr", case_sensitive = false }, ["#FORMAL"] = { parser_function = localization_functions, case_sensitive = true }, ["#FORMATDATE"] = { aliases = {"#DATEFORMAT"}, parser_function = formatting, case_sensitive = false }, ["#IF"] = { parser_function = parser_functions_ext .. "if", case_sensitive = false }, ["#IFEQ"] = { parser_function = parser_functions_ext .. "ifeq", case_sensitive = false }, ["#IFERROR"] = { parser_function = parser_functions_ext .. "iferror", case_sensitive = false }, ["#IFEXIST"] = { parser_function = parser_functions_ext .. "ifexist", case_sensitive = false }, ["#IFEXPR"] = { parser_function = parser_functions_ext .. "ifexpr", case_sensitive = false }, ["#interlanguagelink"] = { -- Case-sensitive lowercase. parser_function = miscellaneous, case_sensitive = true }, ["#interwikilink"] = { -- Case-sensitive lowercase. parser_function = miscellaneous, case_sensitive = true }, ["#INVOKE"] = { parser_function = "mw:Extension:Scribunto#Usage", case_sensitive = false }, ["#LANGUAGE"] = { parser_function = localization_functions, parser_variable = localization_functions, -- Not a typo. case_sensitive = false }, ["#LQTPAGELIMIT"] = { parser_function = liquidthreads, case_sensitive = false }, ["#LST"] = { aliases = {"#SECTION"}, parser_function = labeled_section_transclusion, case_sensitive = false }, ["#LSTH"] = { aliases = {"#SECTION-H"}, parser_function = labeled_section_transclusion, case_sensitive = false }, ["#LSTX"] = { aliases = {"#SECTION-X"}, parser_function = labeled_section_transclusion, case_sensitive = false }, ["#PROPERTY"] = { parser_function = "m:Wikidata/Notes/Inclusion syntax", case_sensitive = false }, ["#REL2ABS"] = { parser_function = parser_functions_ext .. "rel2abs", case_sensitive = false }, ["#SPECIAL"] = { parser_function = miscellaneous, case_sensitive = false }, ["#SPECIALE"] = { parser_function = miscellaneous, case_sensitive = false }, ["#STATEMENTS"] = { parser_function = "d:WD:How to use data on Wikimedia projects#Parser function", case_sensitive = false }, ["#SWITCH"] = { parser_function = parser_functions_ext .. "switch", case_sensitive = false }, ["#TAG"] = { parser_function = miscellaneous, case_sensitive = false }, ["#TARGET"] = { parser_function = "mw:Extension:MassMessage#Parser function delivery lists", case_sensitive = false }, ["#TIME"] = { parser_function = parser_functions_ext .. "time", case_sensitive = false }, ["#timef"] = { -- Case-sensitive lowercase. parser_function = parser_functions_ext, case_sensitive = true }, ["#timefl"] = { -- Case-sensitive lowercase. parser_function = parser_functions_ext, case_sensitive = true }, ["#TIMEL"] = { parser_function = parser_functions_ext .. "timel", case_sensitive = false }, ["#TITLEPARTS"] = { parser_function = parser_functions_ext .. "titleparts", case_sensitive = false }, ["#USELIQUIDTHREADS"] = { parser_function = liquidthreads, case_sensitive = false }, ["="] = { parser_variable = escaped_characters, case_sensitive = false }, ["ANCHORENCODE"] = { parser_function = url_data, case_sensitive = false }, ["ARTICLEPATH"] = { parser_variable = technical_metadata, case_sensitive = false }, ["BASEPAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["BASEPAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["BIDI"] = { parser_function = formatting, case_sensitive = false }, ["CANONICALURL"] = { parser_function = url_data, case_sensitive = false }, ["CANONICALURLE"] = { parser_function = url_data, case_sensitive = false }, ["CASCADINGSOURCES"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["CONTENTLANGUAGE"] = { aliases = {"CONTENTLANG"}, parser_variable = technical_metadata, case_sensitive = true }, ["CURRENTDAY"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTDAY2"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTDAYNAME"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTDOW"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTHOUR"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTMONTH"] = { aliases = {"CURRENTMONTH2"}, parser_variable = date_and_time, case_sensitive = true }, ["CURRENTMONTH1"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTMONTHABBREV"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTMONTHNAME"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTMONTHNAMEGEN"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTTIME"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTTIMESTAMP"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTVERSION"] = { parser_variable = technical_metadata, case_sensitive = true }, ["CURRENTWEEK"] = { parser_variable = date_and_time, case_sensitive = true }, ["CURRENTYEAR"] = { parser_variable = date_and_time, case_sensitive = true }, ["DEFAULTSORT"] = { aliases = {"DEFAULTCATEGORYSORT", "DEFAULTSORTKEY"}, parser_function = technical_metadata, case_sensitive = true }, ["DIRECTIONMARK"] = { aliases = {"DIRMARK"}, parser_variable = technical_metadata, case_sensitive = true }, ["DISPLAYTITLE"] = { parser_function = technical_metadata, case_sensitive = true }, ["FILEPATH"] = { parser_function = url_data, case_sensitive = false }, ["FORMATNUM"] = { parser_function = formatting, case_sensitive = false }, ["FULLPAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["FULLPAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["FULLURL"] = { parser_function = url_data, case_sensitive = false }, ["FULLURLE"] = { parser_function = url_data, case_sensitive = false }, ["GENDER"] = { parser_function = localization_functions, case_sensitive = false }, ["GRAMMAR"] = { parser_function = localization_functions, case_sensitive = false }, ["INT"] = { parser_function = localization_functions, case_sensitive = false }, ["LC"] = { parser_function = formatting, case_sensitive = false }, ["LCFIRST"] = { parser_function = formatting, case_sensitive = false }, ["LOCALDAY"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALDAY2"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALDAYNAME"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALDOW"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALHOUR"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALMONTH"] = { aliases = {"LOCALMONTH2"}, parser_variable = date_and_time, case_sensitive = true }, ["LOCALMONTH1"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALMONTHABBREV"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALMONTHNAME"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALMONTHNAMEGEN"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALTIME"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALTIMESTAMP"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALURL"] = { parser_function = url_data, case_sensitive = false }, ["LOCALURLE"] = { parser_function = url_data, case_sensitive = false }, ["LOCALWEEK"] = { parser_variable = date_and_time, case_sensitive = true }, ["LOCALYEAR"] = { parser_variable = date_and_time, case_sensitive = true }, ["MSG"] = { transclusion_modifier = transclusion_modifiers, priority = 2, case_sensitive = false }, ["MSGNW"] = { transclusion_modifier = transclusion_modifiers, priority = 2, case_sensitive = false }, ["NAMESPACE"] = { parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["NAMESPACEE"] = { parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["NAMESPACENUMBER"] = { parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["NOEXTERNALLANGLINKS"] = { parser_function = noexternallanglinks, parser_variable = noexternallanglinks, case_sensitive = false }, ["NS"] = { parser_function = namespaces_2, case_sensitive = false }, ["NSE"] = { parser_function = namespaces_2, case_sensitive = false }, ["NUMBERINGROUP"] = { aliases = {"NUMINGROUP"}, parser_function = statistics, case_sensitive = true }, ["NUMBEROFACTIVEUSERS"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFADMINS"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFARTICLES"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFEDITS"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFFILES"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFPAGES"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["NUMBEROFUSERS"] = { parser_function = statistics, parser_variable = statistics, case_sensitive = true }, ["PADLEFT"] = { parser_function = formatting, case_sensitive = false }, ["PADRIGHT"] = { parser_function = formatting, case_sensitive = false }, ["PAGEID"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = false }, ["PAGELANGUAGE"] = { parser_variable = technical_metadata, case_sensitive = true }, ["PAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["PAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["PAGESINCATEGORY"] = { aliases = {"PAGESINCAT"}, parser_function = statistics, case_sensitive = true }, ["PAGESIZE"] = { parser_function = technical_metadata_of_another_page, case_sensitive = true }, ["PLURAL"] = { parser_function = localization_functions, case_sensitive = false }, ["PROTECTIONEXPIRY"] = { parser_function = { technical_metadata, technical_metadata_of_another_page }, case_sensitive = true }, ["PROTECTIONLEVEL"] = { parser_function = { technical_metadata, technical_metadata_of_another_page }, case_sensitive = true }, ["RAW"] = { transclusion_modifier = transclusion_modifiers, priority = 3, case_sensitive = false }, ["REVISIONDAY"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONDAY2"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONID"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONMONTH"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONMONTH1"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONSIZE"] = { parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONTIMESTAMP"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONUSER"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["REVISIONYEAR"] = { parser_function = technical_metadata_of_another_page, parser_variable = technical_metadata, case_sensitive = true }, ["ROOTPAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["ROOTPAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["SAFESUBST"] = { transclusion_modifier = substitution, priority = 1, case_sensitive = false }, ["SCRIPTPATH"] = { parser_variable = technical_metadata, case_sensitive = false }, ["SERVER"] = { parser_variable = technical_metadata, case_sensitive = false }, ["SERVERNAME"] = { parser_variable = technical_metadata, case_sensitive = false }, ["SITENAME"] = { parser_variable = technical_metadata, case_sensitive = true }, ["STYLEPATH"] = { parser_variable = technical_metadata, case_sensitive = false }, ["SUBJECTPAGENAME"] = { aliases = {"ARTICLEPAGENAME"}, parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["SUBJECTPAGENAMEE"] = { aliases = {"ARTICLEPAGENAMEE"}, parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["SUBJECTSPACE"] = { aliases = {"ARTICLESPACE"}, parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["SUBJECTSPACEE"] = { aliases = {"ARTICLESPACEE"}, parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["SUBPAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["SUBPAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["SUBST"] = { transclusion_modifier = substitution, priority = 1, case_sensitive = false }, ["TALKPAGENAME"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["TALKPAGENAMEE"] = { parser_function = page_names, parser_variable = page_names, case_sensitive = true }, ["TALKSPACE"] = { parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["TALKSPACEE"] = { parser_function = namespaces, parser_variable = namespaces, case_sensitive = true }, ["UC"] = { parser_function = formatting, case_sensitive = false }, ["UCFIRST"] = { parser_function = formatting, case_sensitive = false }, ["URLENCODE"] = { parser_function = url_data, case_sensitive = false }, ["USERLANGUAGE"] = { parser_variable = localization_variables, case_sensitive = true }, } do data[k] = v if not v.name then v.name = k end local aliases = v.aliases if aliases then for i = 1, #aliases do data[aliases[i]] = v end v.aliases = nil end end return data jkwpl1ihlf560owi79olg91zvhvb1xy Module:table/length 828 8006 54757 2026-09-25T22:06:42Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local ipairs_default_iter = ipairs{} return function(t, raw) local n = 0 if raw then for i in ipairs_default_iter, t, 0 do n = i end return n end repeat n = n + 1 until t[n] == nil return n - 1 end' 54757 Scribunto text/plain local ipairs_default_iter = ipairs{} return function(t, raw) local n = 0 if raw then for i in ipairs_default_iter, t, 0 do n = i end return n end repeat n = n + 1 until t[n] == nil return n - 1 end tryk70hxgidgdgqrb7utnj2sub9w656 Module:title/isTitle 828 8007 54758 2026-09-25T22:07:15Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_metamethod_module = "Module:table/getMetamethod" local error = error local rawequal = rawequal local type = type local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local title_eq, title_lt local function get_title_mm() local success, eq, lt = get_metamethod(mw.title.getCurrentTitle(), "__eq", "__lt") if not success then error("Failed to fetch current title") -- this shou...' 54758 Scribunto text/plain local table_get_metamethod_module = "Module:table/getMetamethod" local error = error local rawequal = rawequal local type = type local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local title_eq, title_lt local function get_title_mm() local success, eq, lt = get_metamethod(mw.title.getCurrentTitle(), "__eq", "__lt") if not success then error("Failed to fetch current title") -- this should never happen end title_eq, title_lt, get_title_mm = eq, lt, nil return eq end --[==[ Returns {true} if the input is a title object, or {false} if not.]==] return function(obj) if not (obj and type(obj) == "table") then return false end local success, eq, lt = get_metamethod(obj, "__eq", "__lt") -- There's no foolproof method for checking for a title object, but the -- __eq and __lt metamethods should always be the same. Also discount titles -- that only have fragments (returned by mw.title.new() and -- mw.title.makeTitle() if the input starts with "#"), since they cannot -- represent a valid page, and are broken in various ways (e.g. attempting -- to access certain keys results in an error); see [[phab:T240678]]. return success and eq ~= nil and lt ~= nil and rawequal(eq, (title_eq or get_title_mm())) and rawequal(lt, title_lt) and obj.prefixedText ~= "" end j1ejjc7c8trmnla3tstyr2or7aay9ft Module:title/redirectTarget 828 8008 54759 2026-09-25T22:08:02Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local pcall = pcall local at_limit local function is_redirect(title) return title.isRedirect end --[==[ Returns the title object of the redirect target if title page is a redirect. Checking {title.redirectTarget} will transclude the title page, which may be undesirable when checking large numbers of titles. To avoid this, this function will attempt to check the {title.isRedirect} key first, only checking {title.redirectTarget} if it is confirmed that the tit...' 54759 Scribunto text/plain local pcall = pcall local at_limit local function is_redirect(title) return title.isRedirect end --[==[ Returns the title object of the redirect target if title page is a redirect. Checking {title.redirectTarget} will transclude the title page, which may be undesirable when checking large numbers of titles. To avoid this, this function will attempt to check the {title.isRedirect} key first, only checking {title.redirectTarget} if it is confirmed that the title is a redirect. However, doing so will increment the expensive function count, which has a limit of 500. Once the limit has been reached, this function reverts to checking {title.redirectTarget} directly instead. The {force_transclusion} flag may be set to force the use of transclusion ({title.redirectTarget}) in all instances, which can be used to avoid incrementing the expensive function count; this is sometimes useful when making a large number of checks, as hitting the expensive function limit can be disruptive to other modules or templates.]==] return function(title, force_transclusion) if not (force_transclusion or at_limit) then local success, result = pcall(is_redirect, title) if not success then at_limit = true elseif not result then return false end end local redirect = title.redirectTarget if not redirect then return false end -- If the original title had a fragment, carry it over to the redirect. local fragment = title.fragment if fragment and fragment ~= "" then redirect.fragment = fragment end return redirect end 0fmvtjkqug22ktp797jksqoi89g47pw Module:title/getCurrentNamespace 828 8009 54760 2026-09-25T22:08:45Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[==[ Returns the data table for the current namespace from {mw.site.namespaces}. This module may be loaded with {mw.loadData}.]==] return mw.site.namespaces[mw.title.getCurrentTitle().namespace]' 54760 Scribunto text/plain --[==[ Returns the data table for the current namespace from {mw.site.namespaces}. This module may be loaded with {mw.loadData}.]==] return mw.site.namespaces[mw.title.getCurrentTitle().namespace] 2udpe5a40x351nzks01by6n1mixwd3o Module:languages/canonical names 828 8010 54761 2026-09-25T22:09:38Z Deadend0914 7211 Gesceop tramet þe hafaþ 'return { ["'Are'are"] = "alu", ["A'ou"] = "aou", ["A-Hmao"] = "hmd", ["A-Pucikwar"] = "apq", ["Aari"] = "aiw", ["Aasax"] = "aas", ["Aba"] = "utp", ["Abaga"] = "abg", ["Abai"] = "poz-abi", ["Abai Sungai"] = "abf", ["Abanyom"] = "abm", ["Abau"] = "aau", ["Abaza"] = "abq", ["Abenaki"] = "abe", ["Abenlen Ayta"] = "abp", ["Abidji"] = "abi", ["Abinomn"] = "bsa", ["Abipón"] = "axb", ["Abishira"] = "ash", ["Abkhaz"] = "ab", ["Abom"] = "aob", ["Abon...' 54761 Scribunto text/plain return { ["'Are'are"] = "alu", ["A'ou"] = "aou", ["A-Hmao"] = "hmd", ["A-Pucikwar"] = "apq", ["Aari"] = "aiw", ["Aasax"] = "aas", ["Aba"] = "utp", ["Abaga"] = "abg", ["Abai"] = "poz-abi", ["Abai Sungai"] = "abf", ["Abanyom"] = "abm", ["Abau"] = "aau", ["Abaza"] = "abq", ["Abenaki"] = "abe", ["Abenlen Ayta"] = "abp", ["Abidji"] = "abi", ["Abinomn"] = "bsa", ["Abipón"] = "axb", ["Abishira"] = "ash", ["Abkhaz"] = "ab", ["Abom"] = "aob", ["Abon"] = "abo", ["Aborlan Tagbanwa"] = "tbw", ["Abu"] = "ado", ["Abu'"] = "aah", ["Abua"] = "abn", ["Abui"] = "abz", ["Abun"] = "kgr", ["Abung"] = "abl", ["Abure"] = "abu", ["Abureni"] = "mgj", ["Abé"] = "aba", ["Acatepec Me'phaa"] = "tpx", ["Acehnese"] = "ace", ["Achagua"] = "aca", ["Achang"] = "acn", ["Ache"] = "yif", ["Acheron"] = "acz", ["Achi"] = "acr", ["Acholi"] = "ach", ["Achuar"] = "acu", ["Achumawi"] = "acv", ["Aché"] = "guq", ["Acroá"] = "acs", ["Adai"] = "xad", ["Adamorobe Sign Language"] = "ads", ["Adang"] = "adn", ["Adangbe"] = "adq", ["Adangme"] = "ada", ["Adap"] = "adp", ["Adasen"] = "tiu", ["Adele"] = "ade", ["Adhola"] = "adh", ["Adi"] = "adi", ["Adioukrou"] = "adj", ["Adithinngithigh"] = "dth", ["Adivasi Odia"] = "ort", ["Adiwasi Garasia"] = "gas", ["Adnyamathanha"] = "adt", ["Adonara"] = "adr", ["Aduge"] = "adu", ["Adzera"] = "adz", ["Adûni"] = "art-adu", ["Aeka"] = "aez", ["Aekyom"] = "awi", ["Aequian"] = "xae", ["Aer"] = "aeq", ["Afade"] = "aal", ["Afar"] = "aa", ["Afghan Sign Language"] = "afg", ["Afitti"] = "aft", ["Afra"] = "ulf", ["Afrihili"] = "afh", ["Afrikaans"] = "af", ["Afro-Seminole Creole"] = "afs", ["Agarabi"] = "agd", ["Agatu"] = "agc", ["Agawam"] = "alg-aga", ["Aghem"] = "agq", ["Aghu"] = "ahh", ["Aghu Tharrnggala"] = "gtu", ["Aghul"] = "agx", ["Aghwan"] = "xag", ["Agi"] = "aif", ["Agob"] = "kit", ["Agoi"] = "ibm", ["Aguacateca"] = "agu", ["Aguaruna"] = "agr", ["Aguna"] = "aug", ["Agusan Manobo"] = "msm", ["Agutaynen"] = "agn", ["Agwagwune"] = "yay", ["Ahanta"] = "aha", ["Ahirani"] = "ahr", ["Ahom"] = "aho", ["Ahtna"] = "aht", ["Ahwai"] = "nfd", ["Ai-Cham"] = "aih", ["Aighon"] = "aix", ["Aikanã"] = "tba", ["Aiklep"] = "mwg", ["Aimele"] = "ail", ["Aimol"] = "aim", ["Ainbai"] = "aic", ["Ainu"] = "ain", ["Aiome"] = "aki", ["Airoran"] = "air", ["Aisi"] = "mmq", ["Aiton"] = "aio", ["Aja (East Africa)"] = "aja", ["Aja (West Africa)"] = "ajg", ["Ajawa"] = "ajw", ["Ajië"] = "aji", ["Ak"] = "akq", ["Aka (Central Africa)"] = "axk", ["Aka (Sudan)"] = "soh", ["Aka-Bea"] = "abj", ["Aka-Bo"] = "akm", ["Aka-Cari"] = "aci", ["Aka-Kede"] = "akx", ["Aka-Kol"] = "aky", ["Aka-Kora"] = "ack", ["Akan"] = "ak", ["Akar-Bale"] = "acl", ["Akaselem"] = "aks", ["Akatek"] = "knj", ["Akawaio"] = "ake", ["Ake"] = "aik", ["Akebu"] = "keu", ["Akei"] = "tsr", ["Akeu"] = "aeu", ["Akha"] = "ahk", ["Akhvakh"] = "akv", ["Akkadian"] = "akk", ["Akkala Sami"] = "sia", ["Aklanon"] = "akl", ["Akolet"] = "akt", ["Akoose"] = "bss", ["Akoye"] = "miw", ["Akpa"] = "akf", ["Akpes"] = "ibe", ["Akrukay"] = "afi", ["Akuku"] = "ayk", ["Akum"] = "aku", ["Akuntsu"] = "aqz", ["Akurio"] = "ako", ["Akwa"] = "akw", ["Akyaung Ari"] = "nqy", ["Al-Sayyid Bedouin Sign Language"] = "syy", ["Alaba"] = "alw", ["Alabama"] = "akz", ["Alabat Island Agta"] = "dul", ["Alacatlatzala Mixtec"] = "mim", ["Alago"] = "ala", ["Alagwa"] = "wbj", ["Alak"] = "alk", ["Alamblak"] = "amp", ["Alangan"] = "alj", ["Alanic"] = "xln", ["Alapmunte"] = "apv", ["Alas-Kluet Batak"] = "btz", ["Alawa"] = "alh", ["Alazapa"] = "nai-ala", ["Albanian"] = "sq", ["Albanian Sign Language"] = "sqk", ["Alchuka"] = "tuw-alk", ["Alcozauca Mixtec"] = "xta", ["Alege"] = "alf", ["Alekano"] = "gah", ["Alemannic German"] = "gsw", ["Aleut"] = "ale", ["Algerian Arabic"] = "arq", ["Algerian Jewish Sign Language"] = "ajs", ["Algerian Sign Language"] = "asp", ["Algonquin"] = "alq", ["Ali"] = "aiy", ["Alladian"] = "ald", ["Allar"] = "all", ["Allentiac"] = "sai-all", ["Alngith"] = "aid", ["Alo Phola"] = "ypo", ["Alorese"] = "aol", ["Aloápam Zapotec"] = "zaq", ["Alsea"] = "aes", ["Alu"] = "mte", ["Alu Kurumba"] = "xua", ["Alugu"] = "aub", ["Alumu-Tesu"] = "aab", ["Alune"] = "alp", ["Alungul"] = "aus-alu", ["Aluo"] = "yna", ["Alur"] = "alz", ["Alutiiq"] = "ems", ["Alutor"] = "alr", ["Alviri-Vidari"] = "avd", ["Alyawarr"] = "aly", ["Ama"] = "amm", ["Amahai"] = "amq", ["Amahuaca"] = "amc", ["Amaimon"] = "ali", ["Amal"] = "aad", ["Amanab"] = "amn", ["Amanayé"] = "ama", ["Amara"] = "aie", ["Amarakaeri"] = "amr", ["Amarasi"] = "aaz", ["Amarizana"] = "awd-ama", ["Amasi"] = "alv-ama", ["Amatlán Zapotec"] = "zpo", ["Amba"] = "rwm", ["Ambai"] = "amk", ["Ambakich"] = "aew", ["Ambala Ayta"] = "abc", ["Ambelau"] = "amv", ["Ambele"] = "ael", ["Amblong"] = "alm", ["Ambo"] = "amb", ["Ambonese Malay"] = "abs", ["Ambrak"] = "aag", ["Ambul"] = "apo", ["Ambulas"] = "abt", ["Amdang"] = "amj", ["Amele"] = "aey", ["American Sign Language"] = "ase", ["Amganad Ifugao"] = "ifa", ["Amharic"] = "am", ["Ami"] = "amy", ["Amis"] = "ami", ["Ammonite"] = "sem-amm", ["Amo"] = "amo", ["Amol"] = "alx", ["Amoltepec Mixtec"] = "mbz", ["Amondawa"] = "adw", ["Amorite"] = "sem-amo", ["Ampanang"] = "apg", ["Ampari Dogon"] = "aqd", ["Amri Karbi"] = "ajz", ["Amto"] = "amt", ["Amurdag"] = "amg", ["Ana Tinga Dogon"] = "dti", ["Anaang"] = "anw", ["Anakalangu"] = "akg", ["Anam"] = "pda", ["Anambé"] = "aan", ["Anamgura"] = "imi", ["Anasi"] = "bpo", ["Anauyá"] = "awd-ana", ["Ancient Greek"] = "grc", ["Ancient Ligurian"] = "xlg", ["Ancient Macedonian"] = "xmk", ["Ancient North Arabian"] = "xna", ["Ancient Zapotec"] = "xzp", ["Andai"] = "afd", ["Andajin"] = "ajn", ["Andalusian Arabic"] = "xaa", ["Andaman Creole Hindi"] = "hca", ["Andaqui"] = "ana", ["Andarum"] = "aod", ["Andegerebinha"] = "adg", ["Andh"] = "anr", ["Andi"] = "ani", ["Andio"] = "bzb", ["Andjingith"] = "aus-and", ["Andoa"] = "anb", ["Andoque"] = "ano", ["Andoquero"] = "sai-and", ["Andra-Hus"] = "anx", ["Aneityum"] = "aty", ["Anem"] = "anz", ["Aneme Wake"] = "aby", ["Anfillo"] = "myo", ["Angaataha"] = "agm", ["Angaité"] = "aqt", ["Angal"] = "age", ["Angal Enen"] = "aoe", ["Angal Heneng"] = "akh", ["Angami"] = "njm", ["Angevin"] = "roa-ang", ["Angguruk Yali"] = "yli", ["Angika"] = "anp", ["Angkamuthi"] = "avm", ["Angkola Batak"] = "akb", ["Angkula"] = "aus-ang", ["Angloromani"] = "rme", ["Angolar"] = "aoa", ["Angor"] = "agg", ["Angoram"] = "aog", ["Angosturas Tunebo"] = "tnd", ["Anguthimri"] = "awg", ["Ani Phowa"] = "ypn", ["Anii"] = "blo", ["Animere"] = "anf", ["Anindilyakwa"] = "aoi", ["Anjam"] = "boj", ["Ankave"] = "aak", ["Anmatyerre"] = "amx", ["Annobonese"] = "fab", ["Anong"] = "nun", ["Anor"] = "anj", ["Anserma"] = "ans", ["Ansus"] = "and", ["Antakarinya"] = "ant", ["Antigua and Barbuda Creole English"] = "aig", ["Antillean Creole"] = "gcf", ["Anu"] = "anl", ["Anuak"] = "anu", ["Anufo"] = "cko", ["Anuki"] = "aui", ["Anus"] = "auq", ["Anuta"] = "aud", ["Anyi"] = "any", ["Anyin Morofo"] = "mtb", ["Anāl"] = "anm", ["Aoheng"] = "pni", ["Aore"] = "aor", ["Ap Ma"] = "kbx", ["Apabhramsa"] = "inc-apa", ["Apalachee"] = "xap", ["Apalaí"] = "apy", ["Apali"] = "ena", ["Apasco-Apoala Mixtec"] = "mip", ["Apatani"] = "apt", ["Apiaká"] = "api", ["Apinayé"] = "apn", ["Apma"] = "app", ["Apolista"] = "awd-apo", ["Apro"] = "ahp", ["Apurinã"] = "apu", ["Apurucayali Ashéninka"] = "cpc", ["Aputai"] = "apx", ["Arabana"] = "ard", ["Arabela"] = "arl", ["Arabic"] = "ar", ["Aragonese"] = "an", ["Araki"] = "akr", ["Arakwal"] = "rkw", ["Aralle-Tabulahan"] = "atq", ["Aramaic"] = "arc", ["Arammba"] = "stk", ["Aranadan"] = "aaf", ["Aranama-Tamique"] = "xrt", ["Arandai"] = "jbj", ["Araona"] = "aro", ["Arapaho"] = "arp", ["Arapaso"] = "arj", ["Arara-Karo"] = "arr", ["Ararandewára"] = "xaj", ["Araweté"] = "awt", ["Arawum"] = "awm", ["Arbore"] = "arv", ["Archi"] = "aqc", ["Are"] = "mwc", ["Areba"] = "aea", ["Arem"] = "aem", ["Argentine Sign Language"] = "aed", ["Argobba"] = "agj", ["Arguni"] = "agf", ["Arhuaco"] = "arh", ["Arhâ"] = "aqr", ["Arhö"] = "aok", ["Ari"] = "aac", ["Aribwatsa"] = "laz", ["Aribwaung"] = "ylu", ["Arifama-Miniafia"] = "aai", ["Arigidi"] = "aqg", ["Arikapú"] = "ark", ["Arikara"] = "ari", ["Arikem"] = "ait", ["Arin"] = "xrn", ["Aringa"] = "luc", ["Armazic"] = "xrm", ["Armenian"] = "hy", ["Armenian Sign Language"] = "aen", ["Aromanian"] = "rup", ["Arop-Lokep"] = "apr", ["Arop-Sissano"] = "aps", ["Arosi"] = "aia", ["Arritinngithigh"] = "rrt", ["Arta"] = "atz", ["Arua"] = "aru", ["Aruamu"] = "msy", ["Aruek"] = "aur", ["Aruop"] = "lsr", ["Arutani"] = "atx", ["Aruá"] = "arx", ["Arára (Mato Grosso)"] = "axg", ["Arára (Pará)"] = "aap", ["As"] = "asz", ["Asaba"] = "seo", ["Asaro'o"] = "mtv", ["Ashe"] = "ahs", ["Ashkun"] = "ask", ["Asho Chin"] = "csh", ["Ashokan Prakrit"] = "inc-ash", ["Ashraaf"] = "cus-ash", ["Asháninka"] = "cni", ["Asi"] = "bno", ["Asilulu"] = "asl", ["Askopan"] = "eiv", ["Asoa"] = "asv", ["Assamese"] = "as", ["Assan"] = "xss", ["Assangori"] = "sjg", ["Assiniboine"] = "asb", ["Assyrian Neo-Aramaic"] = "aii", ["Asturian"] = "ast", ["Asu"] = "aum", ["Asue Awyu"] = "psa", ["Asumboa"] = "aua", ["Asunción Mixtepec Zapotec"] = "zoo", ["Asuri"] = "asr", ["Ata"] = "atm", ["Ata Manobo"] = "atd", ["Atakapa"] = "aqp", ["Atampaya"] = "amz", ["Atanques"] = "cba-ata", ["Atatláhuca Mixtec"] = "mib", ["Atauran"] = "adb", ["Atayal"] = "tay", ["Atemble"] = "ate", ["Ateso"] = "teo", ["Athpare"] = "aph", ["Ati"] = "atk", ["Atikamekw"] = "atj", ["Atohwaim"] = "aqm", ["Atong (Cameroon)"] = "ato", ["Atong (India)"] = "aot", ["Atorada"] = "aox", ["Atsahuaca"] = "atc", ["Atsam"] = "cch", ["Atsugewi"] = "atw", ["Attapady Kurumba"] = "pkr", ["Attié"] = "ati", ["Au"] = "avt", ["Auhelawa"] = "kud", ["Aukan"] = "djk", ["Aulua"] = "aul", ["Aurá"] = "aux", ["Aushi"] = "auh", ["Aushiri"] = "avs", ["Auslan"] = "asf", ["Austral"] = "aut", ["Australian Aboriginal Sign Language"] = "asw", ["Australian Kriol"] = "rop", ["Austrian Sign Language"] = "asq", ["Auwe"] = "smf", ["Auyana"] = "auy", ["Auye"] = "auu", ["Auyokawa"] = "auo", ["Avar"] = "av", ["Avatime"] = "avn", ["Avau"] = "avb", ["Avava"] = "tmb", ["Avestan"] = "ae", ["Avikam"] = "avi", ["Avokaya"] = "avu", ["Avá-Canoeiro"] = "avv", ["Awa (China)"] = "vwa", ["Awa (New Guinea)"] = "awb", ["Awa-Cuaiquer"] = "kwi", ["Awabakal"] = "awk", ["Awadhi"] = "awa", ["Awak"] = "awo", ["Awar"] = "aya", ["Awara"] = "awx", ["Awbono"] = "awh", ["Aweer"] = "bob", ["Awera"] = "awr", ["Awetí"] = "awe", ["Awing"] = "azo", ["Awjila"] = "auj", ["Awngi"] = "awn", ["Awngthim"] = "gwm", ["Awtuw"] = "kmn", ["Awun"] = "aww", ["Awutu"] = "afu", ["Awyi"] = "auw", ["Axamb"] = "ahb", ["Axi"] = "yix", ["Ayabadhu"] = "ayd", ["Ayautla Mazatec"] = "vmy", ["Ayere"] = "aye", ["Ayerrerenge"] = "axe", ["Ayi"] = "ayq", ["Ayizi"] = "yyz", ["Ayizo"] = "ayb", ["Aymara"] = "ay", ["Ayomán"] = "sai-ayo", ["Ayoquesco Zapotec"] = "zaf", ["Ayoreo"] = "ayo", ["Ayu"] = "ayu", ["Ayutla Mixtec"] = "miy", ["Azerbaijani"] = "az", ["Azha"] = "aza", ["Azhe"] = "yiz", ["Azoyú Me'phaa"] = "tpc", ["Baa"] = "kwb", ["Baagandji"] = "drl", ["Baan"] = "bvj", ["Baangi"] = "bqx", ["Baatonum"] = "bba", ["Baba"] = "bbw", ["Baba Malay"] = "mbf", ["Babango"] = "bbm", ["Babanki"] = "bbk", ["Babatana"] = "baa", ["Babine-Witsuwit'en"] = "bcr", ["Babole"] = "bvx", ["Babungo"] = "bav", ["Babuza"] = "bzg", ["Bacama"] = "bcy", ["Bacanese Malay"] = "btj", ["Bactrian"] = "xbc", ["Bada"] = "bhz", ["Badaga"] = "bfq", ["Badanchi"] = "bau", ["Bade"] = "bde", ["Badeshi"] = "bdz", ["Badimaya"] = "bia", ["Badjiri"] = "jbi", ["Baduy"] = "bac", ["Badyara"] = "pbp", ["Baeggu"] = "bvd", ["Baekje"] = "pkc", ["Baelelea"] = "bvc", ["Baenan"] = "sai-bae", ["Baetora"] = "btr", ["Bafanji"] = "bfj", ["Bafaw"] = "bwt", ["Bafia"] = "ksf", ["Bafut"] = "bfd", ["Baga Kaloum"] = "bqf", ["Baga Koga"] = "bgo", ["Baga Manduri"] = "bmd", ["Baga Pokur"] = "bcg", ["Baga Sitemu"] = "bsp", ["Baga Sobané"] = "bsv", ["Bagheli"] = "bfy", ["Bagirmi"] = "bmi", ["Bago-Kusuntu"] = "bqg", ["Bagri"] = "bgq", ["Bagua"] = "sai-bag", ["Bagupi"] = "bpi", ["Bagusa"] = "bqb", ["Bagvalal"] = "kva", ["Baha"] = "yha", ["Baham"] = "bdw", ["Bahamian Creole"] = "bah", ["Baharna Arabic"] = "abv", ["Bahau"] = "bhv", ["Bahinemo"] = "bjh", ["Bahing"] = "bhj", ["Bahnar"] = "bdq", ["Bahonsuai"] = "bsu", ["Bai (South Sudan)"] = "bdj", ["Baibai"] = "bbf", ["Baikeno"] = "bkx", ["Bailang"] = "tbq-blg", ["Baima"] = "bqh", ["Baimak"] = "bmx", ["Bainouk Gubeeher"] = "alv-bgu", ["Bainouk-Gunyaamolo"] = "bcz", ["Bainouk-Gunyuño"] = "bab", ["Bainouk-Samik"] = "bcb", ["Baiso"] = "bsw", ["Baissa Fali"] = "fah", ["Bajan"] = "bjs", ["Bajelani"] = "bjm", ["Bajjika"] = "vjk", ["Baka"] = "bkc", ["Bakairí"] = "bkq", ["Bakaka"] = "bqz", ["Bakhtiari"] = "bqi", ["Baki"] = "bki", ["Bakoko"] = "bkh", ["Bakole"] = "kme", ["Bakpinka"] = "bbs", ["Bakulung"] = "bbu", ["Bakumpai"] = "bkr", ["Bakung"] = "xkl", ["Bakwé"] = "bjw", ["Bala"] = "tuw-bal", ["Balaesang"] = "bls", ["Balangao"] = "blw", ["Balangingi"] = "sse", ["Balanta-Ganja"] = "bjt", ["Balanta-Kentohe"] = "ble", ["Balantak"] = "blz", ["Balau"] = "blg", ["Baldemu"] = "bdn", ["Bali"] = "bcp", ["Baliledo"] = "poz-bal", ["Balinese"] = "ban", ["Balinese Malay"] = "mhp", ["Balkan Gagauz Turkish"] = "bgx", ["Balkan Romani"] = "rmn", ["Balo"] = "bqo", ["Baloi"] = "biz", ["Balong"] = "bnt-bal", ["Balti"] = "bft", ["Baltic Romani"] = "rml", ["Baluan-Pam"] = "blq", ["Baluchi"] = "bal", ["Bamako Sign Language"] = "bog", ["Bamali"] = "bbq", ["Bambalang"] = "bmo", ["Bambam"] = "ptu", ["Bambara"] = "bm", ["Bambassi"] = "myf", ["Bambili-Bambui"] = "baw", ["Bamenyam"] = "bce", ["Bamu"] = "bcf", ["Bamukumbit"] = "bqt", ["Bamum"] = "bax", ["Bamunka"] = "bvm", ["Bamwe"] = "bmg", ["Ban Khor Sign Language"] = "bfk", ["Bana"] = "bcw", ["Banao Itneg"] = "bjx", ["Banaro"] = "byz", ["Banda"] = "bnd", ["Banda Malay"] = "bpq", ["Banda-Bambari"] = "liy", ["Banda-Banda"] = "bpd", ["Banda-Mbrès"] = "bqk", ["Banda-Ndélé"] = "bfl", ["Banda-Yangere"] = "yaj", ["Bandi"] = "bza", ["Bandial"] = "bqj", ["Bandjalang"] = "bdy", ["Bangala"] = "bxg", ["Bangandu"] = "bgf", ["Bangba"] = "bbe", ["Banggai"] = "bgz", ["Bangi"] = "bni", ["Bangime"] = "dba", ["Bangka"] = "mfb", ["Bangolan"] = "bgj", ["Bangru"] = "sit-ban", ["Bangubangu"] = "bnx", ["Bangwinji"] = "bsj", ["Baniva"] = "bvv", ["Baniwa"] = "bwi", ["Baniwa of Guainia"] = "gae", ["Banjarese"] = "bjn", ["Banka"] = "bxw", ["Bankan Tey Dogon"] = "dbw", ["Bankon"] = "abb", ["Banoni"] = "bcm", ["Bantawa"] = "bap", ["Bantayanon"] = "bfx", ["Bantik"] = "bnq", ["Banyumasan"] = "map-bms", ["Baoule"] = "bci", ["Baraamu"] = "brd", ["Barai"] = "bbb", ["Barakai"] = "baj", ["Baram Kayan"] = "kys", ["Barama"] = "bbg", ["Barambu"] = "brm", ["Baramu"] = "bmz", ["Barapasi"] = "brp", ["Baras"] = "brs", ["Barasana"] = "bsn", ["Barbareño"] = "boi", ["Barclayville Grebo"] = "gry", ["Bardi"] = "bcj", ["Barein"] = "bva", ["Bargam"] = "mlp", ["Bari"] = "bfa", ["Bariai"] = "bch", ["Bariji"] = "bjc", ["Barikanchi"] = "bxo", ["Barikewa"] = "jbk", ["Barngarla"] = "bjb", ["Barok"] = "bjk", ["Barombi"] = "bbi", ["Barranbinya"] = "aus-bra", ["Barro Negro Tunebo"] = "tbn", ["Barrow Point"] = "bpt", ["Baruga"] = "bjz", ["Barunggam"] = "aus-brm", ["Baruya"] = "byr", ["Barwe"] = "bwg", ["Barzani Jewish Neo-Aramaic"] = "bjf", ["Baré"] = "bae", ["Barí"] = "mot", ["Basa"] = "bzw", ["Basa-Gumna"] = "bsl", ["Basa-Gurmana"] = "buj", ["Basaa"] = "bas", ["Basap"] = "bdb", ["Basay"] = "byq", ["Bashkardi"] = "bsg", ["Bashkir"] = "ba", ["Basketo"] = "bst", ["Basque"] = "eu", ["Basque-Icelandic Pidgin"] = "crp-bip", ["Bassa"] = "bsq", ["Bassa-Kontagora"] = "bsr", ["Bassari"] = "bsc", ["Bassossi"] = "bsi", ["Bata"] = "bta", ["Bataan Ayta"] = "ayt", ["Batad Ifugao"] = "ifb", ["Batanga"] = "bnm", ["Batek"] = "btq", ["Bateri"] = "btv", ["Bathari"] = "bhm", ["Bati (Cameroon)"] = "btc", ["Bati (Indonesia)"] = "bvt", ["Bats"] = "bbl", ["Batu"] = "btu", ["Batui"] = "zbt", ["Batuley"] = "bay", ["Bau"] = "bbd", ["Bau Bidayuh"] = "sne", ["Bauchi"] = "bsf", ["Bauni"] = "bpe", ["Baure"] = "brg", ["Bauria"] = "bge", ["Bauro"] = "bxa", ["Bauwaki"] = "bwk", ["Bauzi"] = "bvz", ["Bavarian"] = "bar", ["Bawm Chin"] = "bgr", ["Bay Miwok"] = "mkq", ["Bayali"] = "bjy", ["Baybayanon"] = "bvy", ["Baygo"] = "byg", ["Bayogoula"] = "nai-bay", ["Bayono"] = "byl", ["Bayot"] = "bda", ["Bayungu"] = "bxj", ["Bazigar"] = "bfr", ["Beami"] = "beo", ["Beary"] = "dra-bry", ["Beaver"] = "bea", ["Beba"] = "bfp", ["Bebe"] = "bzv", ["Bebele"] = "beb", ["Bebeli"] = "bek", ["Bebil"] = "bxp", ["Bedik"] = "tnr", ["Bedjond"] = "bjv", ["Bedoanas"] = "bed", ["Beeke"] = "bkf", ["Beele"] = "bxq", ["Beembe"] = "beq", ["Beezen"] = "bnz", ["Befang"] = "bby", ["Begbere-Ejar"] = "bqv", ["Beja"] = "bej", ["Bekati'"] = "bei", ["Bekwarra"] = "bkv", ["Bekwel"] = "bkw", ["Belait"] = "beg", ["Belanda Bor"] = "bxb", ["Belanda Viri"] = "bvi", ["Belarusian"] = "be", ["Belhariya"] = "byw", ["Beli (New Guinea)"] = "bey", ["Beli (South Sudan)"] = "blm", ["Belizean Creole"] = "bzj", ["Bellari"] = "brw", ["Belter Creole"] = "art-bel", ["Bemba"] = "bem", ["Bembe"] = "bmb", ["Ben Tey"] = "dbt", ["Bena"] = "yun", ["Benabena"] = "bef", ["Bench"] = "bcq", ["Bende"] = "bdp", ["Bendi"] = "bct", ["Beneraf"] = "bnv", ["Beng"] = "nhb", ["Benga"] = "bng", ["Bengali"] = "bn", ["Benggoi"] = "bgy", ["Bengkala Sign Language"] = "bqy", ["Bentong"] = "bnu", ["Benyadu'"] = "byd", ["Beothuk"] = "bue", ["Bepour"] = "bie", ["Bera"] = "brf", ["Berakou"] = "bxv", ["Berau Malay"] = "bve", ["Berawan"] = "lod", ["Berbice Creole Dutch"] = "brc", ["Bergish"] = "gmw-bgh", ["Berik"] = "bkl", ["Berinomo"] = "bit", ["Berom"] = "bom", ["Berta"] = "wti", ["Berti"] = "byt", ["Besisi"] = "mhe", ["Besme"] = "bes", ["Besoa"] = "bep", ["Betaf"] = "bfe", ["Betawi"] = "bew", ["Bete"] = "byf", ["Bete-Bendi"] = "btt", ["Betoi"] = "sai-bet", ["Betta Kurumba"] = "xub", ["Bezhta"] = "kap", ["Bhadrawahi"] = "bhd", ["Bhalay"] = "bhx", ["Bharia"] = "bha", ["Bhatri"] = "bgw", ["Bhattiyali"] = "bht", ["Bhaya"] = "bhe", ["Bhele"] = "bhy", ["Bhilali"] = "bhi", ["Bhili"] = "bhb", ["Bhojpuri"] = "bho", ["Bhoti Kinnauri"] = "nes", ["Bhunjia"] = "bhu", ["Biafada"] = "bif", ["Biage"] = "bdf", ["Biak"] = "bhw", ["Biali"] = "beh", ["Bian Marind"] = "bpv", ["Biangai"] = "big", ["Biao"] = "byk", ["Biao Mon"] = "bmt", ["Biao-Jiao Mien"] = "bje", ["Biatah Bidayuh"] = "bth", ["Bibaali"] = "bcn", ["Bibbulman"] = "xbp", ["Bidiyo"] = "bid", ["Bidyara"] = "bym", ["Bidyogo"] = "bjg", ["Biem"] = "bmc", ["Bierebo"] = "bnk", ["Bieria"] = "brj", ["Biete"] = "biu", ["Big Nambas"] = "nmb", ["Biga"] = "bhc", ["Bigambal"] = "xbe", ["Bih"] = "ibh", ["Bihari"] = "bh", ["Bijori"] = "bix", ["Bikya"] = "byb", ["Bila"] = "bip", ["Bilaspuri"] = "kfs", ["Bilba"] = "bpz", ["Bilbil"] = "brz", ["Bile"] = "bil", ["Biliau"] = "bcu", ["Biloxi"] = "bll", ["Bilua"] = "blb", ["Bilur"] = "bxf", ["Bima"] = "bhp", ["Bimin"] = "bhl", ["Bimoba"] = "bim", ["Bina"] = "bmn", ["Binahari"] = "bxz", ["Binandere"] = "bhg", ["Binawa"] = "byj", ["Bindal"] = "xbd", ["Bine"] = "bon", ["Binji"] = "bpj", ["Binongan Itneg"] = "itb", ["Bintauna"] = "bne", ["Bintulu"] = "bny", ["Binukid"] = "bkd", ["Binumarien"] = "bjr", ["Bipi"] = "biq", ["Birao"] = "brr", ["Birgid"] = "brk", ["Birgit"] = "btf", ["Birhor"] = "biy", ["Biri"] = "bzr", ["Biritai"] = "bqq", ["Birri"] = "bvq", ["Birrpayi"] = "xbj", ["Birwa"] = "brl", ["Biseni"] = "ije", ["Bishnupriya Manipuri"] = "bpy", ["Bishuo"] = "bwh", ["Bisis"] = "bnw", ["Bislama"] = "bi", ["Bisorio"] = "bir", ["Bissa"] = "bib", ["Bisu"] = "bzi", ["Bit"] = "bgk", ["Bitare"] = "brt", ["Bitur"] = "mcc", ["Biwat"] = "bwm", ["Biyo"] = "byo", ["Biyom"] = "bpm", ["Blablanga"] = "blp", ["Black Speech"] = "art-bsp", ["Blackfoot"] = "bla", ["Blafe"] = "bfh", ["Blagar"] = "beu", ["Blang"] = "blr", ["Blin"] = "byn", ["Bo"] = "bgl", ["Bo-Rukul"] = "mae", ["Bo-Ung"] = "mux", ["Boano (Maluku)"] = "bzn", ["Boano (Sulawesi)"] = "bzl", ["Bobongko"] = "bgb", ["Bobot"] = "bty", ["Bodo (Central Africa)"] = "boy", ["Bodo (India)"] = "brx", ["Bodo Gadaba"] = "gbj", ["Bodo Parja"] = "bdv", ["Bofi"] = "bff", ["Boga"] = "bvw", ["Bogaya"] = "boq", ["Boghom"] = "bux", ["Boguru"] = "bqu", ["Bohtan Neo-Aramaic"] = "bhn", ["Boikin"] = "bzf", ["Bokar"] = "sit-bok", ["Bokha"] = "ybk", ["Boko"] = "bqc", ["Bokobaru"] = "bus", ["Bokoto"] = "bdt", ["Bokyi"] = "bky", ["Bola"] = "bnp", ["Bolak"] = "art-blk", ["Bolango"] = "bld", ["Bole"] = "bol", ["Bolgo"] = "bvo", ["Bolia"] = "bli", ["Bolinao"] = "smk", ["Bolivian Sign Language"] = "bvl", ["Boloki"] = "bkt", ["Bolon"] = "bof", ["Bolondo"] = "bzm", ["Bolyu"] = "ply", ["Bom"] = "bmf", ["Boma Nkuu"] = "bnt-bon", ["Boma Yumu"] = "bnt-boy", ["Bomboli"] = "bml", ["Bomboma"] = "bws", ["Bomitaba"] = "zmx", ["Bomu"] = "bmq", ["Bomwali"] = "bmw", ["Bon Gula"] = "glc", ["Bonan"] = "peh", ["Bondei"] = "bou", ["Bondo"] = "bfw", ["Bondoukou Kulango"] = "kzc", ["Bondum Dom Dogon"] = "dbu", ["Bonerate"] = "bna", ["Bonggi"] = "bdg", ["Bonggo"] = "bpg", ["Bongili"] = "bui", ["Bongo"] = "bot", ["Bongu"] = "bpu", ["Bonjo"] = "bok", ["Bonkeng"] = "bvg", ["Bonkiman"] = "bop", ["Bookan"] = "bnb", ["Boon"] = "bnl", ["Boor"] = "bvf", ["Bora"] = "boa", ["Border Kuna"] = "kvn", ["Borei"] = "gai", ["Boro"] = "xxb", ["Borong"] = "ksr", ["Boruca"] = "brn", ["Borôro"] = "bor", ["Boselewa"] = "bwf", ["Bosngun"] = "bqs", ["Bote-Majhi"] = "bmj", ["Botlikh"] = "bph", ["Botolan Sambal"] = "sbl", ["Bouna Kulango"] = "nku", ["Bouni"] = "suo", ["Bourbonnais-Berrichon"] = "roa-bbn", ["Bourguignon"] = "roa-brg", ["Bouyei"] = "pcc", ["Bozaba"] = "bzo", ["Bragat"] = "aof", ["Brahui"] = "brh", ["Braj"] = "bra", ["Brazilian Sign Language"] = "bzs", ["Brek Karen"] = "kvl", ["Brem"] = "buq", ["Breri"] = "brq", ["Breton"] = "br", ["Bribri"] = "bzd", ["British Sign Language"] = "bfi", ["Brokkat"] = "bro", ["Brokpake"] = "sgt", ["Brokskat"] = "bkk", ["Brooke's Point Palawano"] = "plw", ["Broome Pearling Lugger Pidgin"] = "bpl", ["Brunei Bisaya"] = "bsb", ["Brunei Malay"] = "kxd", ["Bruny Island"] = "xpz", ["Bu"] = "jid", ["Bu-Nao Bunu"] = "bwx", ["Bua"] = "bub", ["Bualkhaw Chin"] = "cbl", ["Buamu"] = "box", ["Bube"] = "bvb", ["Bubi"] = "buw", ["Bubia"] = "bbx", ["Budeh Stieng"] = "stt", ["Budibud"] = "btp", ["Budong-Budong"] = "bdx", ["Budu"] = "buu", ["Budukh"] = "bdk", ["Buduma"] = "bdm", ["Budza"] = "bja", ["Buena Vista Yokuts"] = "yok-bvy", ["Bugan"] = "bbh", ["Bughotu"] = "bgt", ["Buginese"] = "bug", ["Buglere"] = "sab", ["Bugun"] = "bgg", ["Buhi'non Bikol"] = "ubl", ["Buhid"] = "bku", ["Buhutu"] = "bxh", ["Bujhyal"] = "byh", ["Bukar-Sadung Bidayuh"] = "sdo", ["Bukat"] = "bvk", ["Bukawa"] = "buk", ["Bukhari"] = "bhh", ["Bukit Malay"] = "bvu", ["Bukitan"] = "bkn", ["Bukiyip"] = "ape", ["Buksa"] = "tkb", ["Bukusu"] = "bxk", ["Bulgar"] = "xbo", ["Bulgarian"] = "bg", ["Bulgarian Sign Language"] = "bqn", ["Bulgebi"] = "bmp", ["Buli (Ghana)"] = "bwu", ["Buli (Indonesia)"] = "bzq", ["Bulo Stieng"] = "sti", ["Bulu (Cameroon)"] = "bum", ["Bulu (New Guinea)"] = "bjl", ["Bulungan"] = "blj", ["Bum"] = "bmv", ["Bumaji"] = "byp", ["Bumang"] = "bvp", ["Bumbita Arapesh"] = "aon", ["Bumthangkha"] = "kjz", ["Bun"] = "buv", ["Buna"] = "bvn", ["Bunaba"] = "bck", ["Bunak"] = "bfn", ["Bunama"] = "bdd", ["Bundeli"] = "bns", ["Bung"] = "bqd", ["Bungain"] = "but", ["Bunganditj"] = "xbg", ["Bungku"] = "bkz", ["Bungu"] = "wun", ["Bunoge"] = "dgb", ["Bunun"] = "bnn", ["Buol"] = "blf", ["Bura"] = "bwr", ["Bura Mabang"] = "mde", ["Burak"] = "bys", ["Buraka"] = "bkg", ["Burarra"] = "bvr", ["Burate"] = "bti", ["Burduna"] = "bxn", ["Bure"] = "bvh", ["Burgundian"] = "gme-bur", ["Burji"] = "bji", ["Burmbar"] = "vrt", ["Burmese"] = "my", ["Burmeso"] = "bzu", ["Buru (Indonesia)"] = "mhs", ["Buru (Nigeria)"] = "bqw", ["Burui"] = "bry", ["Burumakok"] = "aip", ["Burun"] = "bdi", ["Burundian Sign Language"] = "lsb", ["Burunge"] = "bds", ["Burushaski"] = "bsk", ["Burusu"] = "bqr", ["Buruwai"] = "asi", ["Buryat"] = "bua", ["Busa"] = "bqp", ["Busam"] = "bxs", ["Busami"] = "bsm", ["Busang Kayan"] = "bfg", ["Bushoong"] = "buf", ["Buso"] = "bso", ["Busoa"] = "bup", ["Bussa"] = "dox", ["Busuu"] = "bju", ["Butbut Kalinga"] = "kyb", ["Butchulla"] = "xby", ["Butmas-Tur"] = "bnr", ["Butuanon"] = "btw", ["Buwal"] = "bhs", ["Buyeo"] = "xpy", ["Buyu"] = "byi", ["Buyuan Jino"] = "jiy", ["Bwa"] = "bww", ["Bwaidoka"] = "bwd", ["Bwala"] = "bnt-bwa", ["Bwanabwana"] = "tte", ["Bwatoo"] = "bwa", ["Bwe Karen"] = "bwe", ["Bwela"] = "bwl", ["Bwile"] = "bwc", ["Bwisi"] = "bwz", ["Byangsi"] = "bee", ["Byep"] = "mkk", ["Bädi Kanum"] = "khd", ["Caac"] = "msq", ["Cabiyarí"] = "cbb", ["Cabre"] = "awd-cab", ["Cabécar"] = "cjp", ["Cacaloxtepec Mixtec"] = "miu", ["Cacaopera"] = "ccr", ["Cacgia Roglai"] = "roc", ["Cacua"] = "cbv", ["Cacán"] = "sai-cac", ["Caddo"] = "cad", ["Cafundó"] = "ccd", ["Cahuarano"] = "cah", ["Cahuilla"] = "chl", ["Caijia"] = "sit-cai", ["Cajonos Zapotec"] = "zad", ["Caka"] = "ckx", ["Cakfem-Mushere"] = "cky", ["Calabrian Greek"] = "grk-cal", ["Calamian Tagbanwa"] = "tbk", ["Calusa"] = "nai-cal", ["Caluyanun"] = "clu", ["Caló"] = "rmq", ["Camarines Norte Agta"] = "abd", ["Cambodian Sign Language"] = "csx", ["Cameroon Mambila"] = "mcu", ["Cameroon Pidgin"] = "wes", ["Campalagian"] = "cml", ["Camsá"] = "kbh", ["Camtho"] = "cmt", ["Camunic"] = "xcc", ["Candoshi-Shapra"] = "cbu", ["Canela"] = "ram", ["Canichana"] = "caz", ["Cantabrian"] = "roa-can", ["Cantonese"] = "yue", ["Cao Miao"] = "cov", ["Caolan"] = "mlc", ["Capanahua"] = "kaq", ["Capiznon"] = "cps", ["Cappadocian Greek"] = "cpg", ["Caquinte"] = "cot", ["Car Nicobarese"] = "caq", ["Cara"] = "cfd", ["Carabayo"] = "cby", ["Caramanta"] = "crf", ["Caranqui"] = "sai-caq", ["Carapana"] = "cbc", ["Cargesian Greek"] = "grk-car", ["Carian"] = "xcr", ["Cariay"] = "awd-kar", ["Caribbean Hindustani"] = "hns", ["Caribbean Javanese"] = "jvn", ["Carijona"] = "cbd", ["Carolina Algonquian"] = "crr", ["Carolinian"] = "cal", ["Carpathian Romani"] = "rmc", ["Carpathian Rusyn"] = "rue", ["Carrier"] = "crx", ["Cashibo-Cacataibo"] = "cbr", ["Cashinahua"] = "cbs", ["Casiguran Dumagat Agta"] = "dgc", ["Casuarina Coast Asmat"] = "asc", ["Catacao"] = "sai-cat", ["Catalan"] = "ca", ["Catalan Sign Language"] = "csc", ["Catawba"] = "chc", ["Catuquinaru"] = "sai-ctq", ["Catío Chibcha"] = "cba-cat", ["Cavineña"] = "cav", ["Cayubaba"] = "cyb", ["Cayuga"] = "cay", ["Cayuse"] = "xcy", ["Cazcan"] = "azc-caz", ["Cañari"] = "sai-cnr", ["Cebaara"] = "sef", ["Cebuano"] = "ceb", ["Celtiberian"] = "xce", ["Cen"] = "cen", ["Central Asmat"] = "cns", ["Central Atlas Tamazight"] = "tzm", ["Central Awyu"] = "awu", ["Central Bai"] = "bca", ["Central Bikol"] = "bcl", ["Central Bontoc"] = "lbk", ["Central Cagayan Agta"] = "agt", ["Central Dusun"] = "dtp", ["Central Franconian"] = "gmw-cfr", ["Central Grebo"] = "grv", ["Central Huasteca Nahuatl"] = "nch", ["Central Huishui Hmong"] = "hmc", ["Central Kurdish"] = "ckb", ["Central Mahuatlán Zapotec"] = "zam", ["Central Malay"] = "pse", ["Central Mansi"] = "mns-cen", ["Central Masela"] = "mxz", ["Central Mashan Hmong"] = "hmm", ["Central Mazahua"] = "maz", ["Central Melanau"] = "mel", ["Central Min"] = "czo", ["Central Mnong"] = "cmo", ["Central Nahuatl"] = "nhn", ["Central Nicobarese"] = "ncb", ["Central Ojibwa"] = "ojc", ["Central Palawano"] = "plc", ["Central Pame"] = "pbs", ["Central Pomo"] = "poo", ["Central Puebla Nahuatl"] = "ncx", ["Central Sama"] = "sml", ["Central Siberian Yupik"] = "ess", ["Central Sierra Miwok"] = "csm", ["Central Subanen"] = "syb", ["Central Tagbanwa"] = "tgt", ["Central Tarahumara"] = "tar", ["Central Teke"] = "nzu", ["Central Tunebo"] = "tuf", ["Centúúm"] = "cet", ["Cerma"] = "cme", ["Ch'olti'"] = "myn-chl", ["Ch'orti'"] = "caa", ["Chaap Wuurong"] = "tjw", ["Chachi"] = "cbi", ["Chadian Arabic"] = "shu", ["Chadian Sign Language"] = "cds", ["Chadong"] = "cdy", ["Chagatai"] = "chg", ["Chaha"] = "sem-cha", ["Chaima"] = "ciy", ["Chairel"] = "sit-cha", ["Chak"] = "ckh", ["Chakali"] = "cli", ["Chakma"] = "ccp", ["Chala"] = "cll", ["Chaldean Neo-Aramaic"] = "cld", ["Chali"] = "tgf", ["Chamacoco"] = "ceg", ["Chamalal"] = "cji", ["Chamba Daka"] = "ccg", ["Chamba Leko"] = "ndi", ["Chambeali"] = "cdh", ["Chambri"] = "can", ["Chamicuro"] = "ccc", ["Chamling"] = "rab", ["Chamorro"] = "ch", ["Champenois"] = "roa-cha", ["Chang"] = "nbc", ["Changriwa"] = "cga", ["Changthang"] = "cna", ["Chantyal"] = "chx", ["Chaná"] = "sai-chn", ["Chané"] = "caj", ["Chapacura"] = "sai-chp", ["Chara"] = "cra", ["Charrua"] = "sai-chr", ["Chaudangsi"] = "cdn", ["Chaura"] = "crv", ["Chavacano"] = "cbk", ["Chayahuita"] = "cbt", ["Chayuco Mixtec"] = "mih", ["Chazumba Mixtec"] = "xtb", ["Che"] = "ruk", ["Chechen"] = "ce", ["Cheke Holo"] = "mrn", ["Chemakum"] = "xch", ["Chenapian"] = "cjn", ["Chenchu"] = "cde", ["Chenoua"] = "cnu", ["Chepang"] = "cdm", ["Chepya"] = "ycp", ["Cherepon"] = "cpn", ["Cherokee"] = "chr", ["Chesu"] = "ych", ["Chetco-Tolowa"] = "ctc", ["Chewong"] = "cwg", ["Cheyenne"] = "chy", ["Chhattisgarhi"] = "hne", ["Chhintange"] = "ctn", ["Chhulung"] = "cur", ["Chiangmai Sign Language"] = "csd", ["Chiapanec"] = "cip", ["Chibcha"] = "chb", ["Chicahuaxtla Triqui"] = "trs", ["Chichewa"] = "ny", ["Chichicapan Zapotec"] = "zpv", ["Chichimeca-Jonaz"] = "pei", ["Chichonyi-Chidzihana-Chikauma"] = "coh", ["Chickasaw"] = "cic", ["Chicomuceltec"] = "cob", ["Chiduruma"] = "dug", ["Chigmecatitlán Mixtec"] = "mii", ["Chilcotin"] = "clc", ["Chilean Sign Language"] = "csg", ["Chilisso"] = "clh", ["Chiltepec Chinantec"] = "csa", ["Chimalapa Zoque"] = "zoh", ["Chimariko"] = "cid", ["Chimila"] = "cbg", ["Chimwiini"] = "bnt-cmw", ["Chinali"] = "cih", ["Chinbon Chin"] = "cnb", ["Chinese"] = "zh", ["Chinese Pidgin English"] = "cpi", ["Chinese Pidgin Russian"] = "crp-cpr", ["Chinese Sign Language"] = "csl", ["Chinook"] = "chh", ["Chinook Jargon"] = "chn", ["Chipaya"] = "cap", ["Chipewyan"] = "chp", ["Chiquihuitlán Mazatec"] = "maq", ["Chiquimulilla"] = "nai-chi", ["Chiquitano"] = "cax", ["Chiricahua"] = "apm", ["Chirino"] = "sai-chi", ["Chiripá"] = "nhd", ["Chiru"] = "cdf", ["Chitimacha"] = "ctm", ["Chitkuli Kinnauri"] = "cik", ["Chittagonian"] = "ctg", ["Chitwania Tharu"] = "the", ["Chiwere"] = "iow", ["Choapan Zapotec"] = "zpc", ["Chocangaca"] = "cgk", ["Chochotec"] = "coz", ["Choctaw"] = "cho", ["Chodri"] = "cdi", ["Chokri Naga"] = "nri", ["Chokwe"] = "cjk", ["Chol"] = "ctu", ["Cholón"] = "cht", ["Chong"] = "cog", ["Choni"] = "cda", ["Chono"] = "sai-cno", ["Chontal Maya"] = "chf", ["Chopi"] = "cce", ["Chothe Naga"] = "nct", ["Chrau"] = "crw", ["Chru"] = "cje", ["Chuabo"] = "chw", ["Chuanqiandian Cluster Miao"] = "cqd", ["Chuave"] = "cjv", ["Chug"] = "cvg", ["Chuj"] = "cac", ["Chuka"] = "cuh", ["Chukchi"] = "ckt", ["Chukwa"] = "cuw", ["Chulym"] = "clw", ["Chumburung"] = "ncu", ["Chungli Ao"] = "njo-jgl", ["Churahi"] = "cdj", ["Church Slavonic"] = "zls-chs", ["Churuya"] = "sai-chu", ["Chut"] = "scb", ["Chuukese"] = "chk", ["Chuvan"] = "xcv", ["Chuvash"] = "cv", ["Chácobo"] = "cao", ["Ci Gbe"] = "cib", ["Cia-Cia"] = "cia", ["Cibak"] = "ckl", ["Cicipu"] = "awc", ["Ciguayo"] = "nai-cig", ["Cimbrian"] = "cim", ["Cinamiguin Manobo"] = "mkx", ["Cinda-Regi-Tiyal"] = "cdr", ["Cineni"] = "cie", ["Cinta Larga"] = "cin", ["Cishingini"] = "asg", ["Citak"] = "txt", ["Ciwogai"] = "tgd", ["Classical Chinese"] = "lzh", ["Classical Gaelic"] = "ghc", ["Classical Guarani"] = "gn-cls", ["Classical Mandaic"] = "myz", ["Classical Mongolian"] = "cmg", ["Classical Nahuatl"] = "nci", ["Classical Newar"] = "nwc", ["Classical Quechua"] = "qwc", ["Classical Syriac"] = "syc", ["Classical Tibetan"] = "xct", ["Coahuilteco"] = "xcw", ["Coast Miwok"] = "csi", ["Coastal Kadazan"] = "kzj", ["Coastal Konjo"] = "kjc", ["Coatecas Altas Zapotec"] = "zca", ["Coatepec Nahuatl"] = "naz", ["Coatlán Mixe"] = "mco", ["Coatlán Zapotec"] = "zps", ["Coatzospan Mixtec"] = "miz", ["Cocama"] = "cod", ["Cochimi"] = "coj", ["Cocopa"] = "coc", ["Cocos Islands Malay"] = "coa", ["Coeruna"] = "sai-coe", ["Coeur d'Alene"] = "crd", ["Cofán"] = "con", ["Cogui"] = "kog", ["Col"] = "liw", ["Colombian Sign Language"] = "csn", ["Colonia Tovar German"] = "gct", ["Columbia-Wenatchi"] = "col", ["Colán"] = "sai-col", ["Comaltepec Chinantec"] = "cco", ["Comanche"] = "com", ["Comechingon"] = "sai-cmg", ["Comecrudo"] = "xcm", ["Communicationssprache"] = "art-com", ["Como Karim"] = "cfg", ["Comox"] = "coo", ["Con"] = "cno", ["Coos"] = "csz", ["Copainalá Zoque"] = "zoc", ["Copala Triqui"] = "trc", ["Copallén"] = "sai-cop", ["Coptic"] = "cop", ["Coquille"] = "coq", ["Cora"] = "crn", ["Cori"] = "cry", ["Cornish"] = "kw", ["Coroado Puri"] = "sai-crd", ["Corsican"] = "co", ["Cosoleacaque Nahuatl"] = "nhk", ["Costa Rican Sign Language"] = "csr", ["Cotabato Manobo"] = "mta", ["Cotoname"] = "xcn", ["Cowlitz"] = "cow", ["Coyaima"] = "coy", ["Coyotepec Popoloca"] = "pbf", ["Coyutla Totonac"] = "toc", ["Cree"] = "cr", ["Creek"] = "mus", ["Crimean Gothic"] = "gme-cgo", ["Crimean Tatar"] = "crh", ["Croatian Sign Language"] = "csq", ["Cross River Mbembe"] = "mfn", ["Crow"] = "cro", ["Cruzeño"] = "crz", ["Cua"] = "cua", ["Cuban Sign Language"] = "csf", ["Cubeo"] = "cub", ["Cueva"] = "sai-cva", ["Cuiba"] = "cui", ["Cuitlatec"] = "cuy", ["Culina"] = "cul", ["Culli"] = "sai-cul", ["Cumanagoto"] = "cuo", ["Cumbric"] = "xcb", ["Cun"] = "cuq", ["Cung"] = "cug", ["Cupeño"] = "cup", ["Curonian"] = "xcu", ["Curripaco"] = "kpc", ["Cutchi-Swahili"] = "ccl", ["Cuvok"] = "cuv", ["Cuyamecalco Mixtec"] = "xtu", ["Cuyunon"] = "cyo", ["Cwi Bwamu"] = "bwy", ["Cypriot Arabic"] = "acy", ["Czech"] = "cs", ["Czech Sign Language"] = "cse", ["Cèmuhî"] = "cam", ["Cốông"] = "cnc", ["Da'a Kaili"] = "kzf", ["Daai Chin"] = "dao", ["Daakaka"] = "bpa", ["Daantanai'"] = "lni", ["Daasanach"] = "dsh", ["Daba"] = "dbq", ["Dabarre"] = "dbr", ["Dabe"] = "dbe", ["Dacian"] = "xdc", ["Dadanitic"] = "sem-dad", ["Dadi Dadi"] = "dda", ["Dadibi"] = "mps", ["Dadiya"] = "dbd", ["Daga"] = "dgz", ["Dagaari Dioula"] = "dgd", ["Dagba"] = "dgk", ["Dagbani"] = "dag", ["Dagik"] = "dec", ["Dagoman"] = "dgn", ["Dahalik"] = "dlk", ["Dahalo"] = "dal", ["Daho-Doo"] = "das", ["Dai"] = "dij", ["Dair"] = "drb", ["Dairi Batak"] = "btd", ["Dakka"] = "dkk", ["Dakota"] = "dak", ["Dakpa"] = "dka", ["Dalmatian"] = "dlm", ["Daloa Bété"] = "bev", ["Dama (Nigeria)"] = "dmm", ["Dama (Sierra Leone)"] = "dmn-dam", ["Damakawa"] = "dam", ["Damal"] = "uhn", ["Dambi"] = "dac", ["Dameli"] = "dml", ["Dampelas"] = "dms", ["Dan"] = "dnj", ["Danaru"] = "dnr", ["Danau"] = "dnu", ["Dandami Maria"] = "daq", ["Dangaléat"] = "daa", ["Dangaura Tharu"] = "thl", ["Danish"] = "da", ["Danish Sign Language"] = "dsl", ["Dano"] = "aso", ["Danu"] = "dnv", ["Danuwar"] = "dhw", ["Dao"] = "daz", ["Daonda"] = "dnd", ["Dar Daju Daju"] = "djc", ["Dar Fur Daju"] = "daj", ["Dar Sila Daju"] = "dau", ["Darai"] = "dry", ["Dargwa"] = "dar", ["Darkinjung"] = "xda", ["Darlong"] = "dln", ["Darmiya"] = "drd", ["Daro-Matu Melanau"] = "dro", ["Darumbal"] = "xgm", ["Dass"] = "dot", ["Datian Min"] = "nan-dat", ["Datooga"] = "tcc", ["Daungwurrung"] = "dgw", ["Daur"] = "dta", ["Davawenyo"] = "daw", ["Dawawa"] = "dww", ["Dawera-Daweloor"] = "ddw", ["Dawro"] = "dwr", ["Day"] = "dai", ["Dayi"] = "dax", ["Dazaga"] = "dzg", ["Deccani"] = "dcc", ["Dedua"] = "ded", ["Defaka"] = "afn", ["Defi Gbe"] = "gbh", ["Deg"] = "mzw", ["Deg Xinag"] = "ing", ["Degema"] = "deg", ["Degenan"] = "dge", ["Dehwari"] = "deh", ["Dela-Oenale"] = "row", ["Delo"] = "ntr", ["Delta Yokuts"] = "yok-dly", ["Dem"] = "dem", ["Dema"] = "dmx", ["Demisa"] = "dei", ["Demotic Egyptian"] = "egx-dem", ["Demta"] = "dmy", ["Dena'ina"] = "tfn", ["Dendi"] = "ddn", ["Dengese"] = "dez", ["Dengka"] = "dnk", ["Deno"] = "dbb", ["Denya"] = "anv", ["Dení"] = "dny", ["Deori"] = "der", ["Desano"] = "des", ["Desiya"] = "dso", ["Dewas Rai"] = "dwz", ["Dewoin"] = "dee", ["Dezfuli"] = "def", ["Dghwede"] = "dgh", ["Dhaiso"] = "dhs", ["Dhalandji"] = "dhl", ["Dhangu"] = "dhg", ["Dhanki"] = "dhn", ["Dhao"] = "nfa", ["Dharug"] = "xdk", ["Dhatki"] = "mki", ["Dhimal"] = "dhi", ["Dhivehi"] = "dv", ["Dhodia"] = "dho", ["Dhofari Arabic"] = "adf", ["Dhudhuroa"] = "ddr", ["Dhundhari"] = "dhd", ["Dhungaloo"] = "dhx", ["Dhurga"] = "dhu", ["Dhuwal"] = "dwu", ["Dhuwaya"] = "dwy", ["Dia"] = "dia", ["Dibabawon Manobo"] = "mbd", ["Dibiyaso"] = "dby", ["Dibo"] = "dio", ["Dicamay Agta"] = "duy", ["Didinga"] = "did", ["Dieri"] = "dif", ["Digo"] = "dig", ["Dii"] = "dur", ["Dijim-Bwilim"] = "cfa", ["Dilling"] = "dil", ["Dima"] = "jma", ["Dimasa"] = "dis", ["Dimbong"] = "dii", ["Dime"] = "dim", ["Dinapigue Agta"] = "phi-din", ["Dineor"] = "mrx", ["Ding"] = "diz", ["Dinka"] = "din", ["Diodio"] = "ddi", ["Dirasha"] = "gdl", ["Diri"] = "dwa", ["Dirim"] = "dir", ["Disa"] = "dsi", ["Ditammari"] = "tbz", ["Ditidaht"] = "dtd", ["Diuwe"] = "diy", ["Diuxi-Tilantongo Mixtec"] = "xtd", ["Dixon Reef"] = "dix", ["Dizin"] = "mdx", ["Djadjawurrung"] = "dja", ["Djambarrpuyngu"] = "djr", ["Djangun"] = "djf", ["Djauan"] = "djn", ["Djawi"] = "djw", ["Djimini"] = "dyi", ["Djinang"] = "dji", ["Djinba"] = "djb", ["Djiwarli"] = "djl", ["Dobel"] = "kvo", ["Dobu"] = "dob", ["Doe"] = "doe", ["Doga"] = "dgg", ["Doghoro"] = "dgx", ["Dogoso"] = "dgs", ["Dogosé"] = "dos", ["Dogri"] = "doi", ["Dogrib"] = "dgr", ["Dogul Dom"] = "dbg", ["Doka"] = "dbi", ["Doko-Uyanga"] = "uya", ["Dolgan"] = "dlg", ["Dom"] = "doa", ["Domaaki"] = "dmk", ["Domari"] = "rmt", ["Dominican Sign Language"] = "doq", ["Dompo"] = "doy", ["Domu"] = "dof", ["Domung"] = "dev", ["Dondo"] = "dok", ["Dong"] = "doh", ["Dongo"] = "doo", ["Dongolawi"] = "kzh", ["Dongotono"] = "ddd", ["Dongshanba Lalo"] = "yik", ["Dongxiang"] = "sce", ["Donno So Dogon"] = "dds", ["Doondo"] = "dde", ["Dorasque"] = "cba-dor", ["Dori'o"] = "dor", ["Dorig"] = "wwo", ["Doromu-Koki"] = "kqc", ["Dorze"] = "doz", ["Doso"] = "dol", ["Doteli"] = "dty", ["Dothraki"] = "art-dtk", ["Doura"] = "don", ["Doutai"] = "tds", ["Doyayo"] = "dow", ["Drehu"] = "dhv", ["Drung"] = "duu", ["Duala"] = "dua", ["Duano"] = "dup", ["Duau"] = "dva", ["Dubli"] = "dub", ["Dubu"] = "dmu", ["Dugun"] = "ndu", ["Duguri"] = "dbm", ["Dugwor"] = "dme", ["Duhwa"] = "kbz", ["Duit"] = "cba-dui", ["Duke"] = "nke", ["Dukhan"] = "trk-dkh", ["Dulbu"] = "dbo", ["Duli"] = "duz", ["Duma"] = "dma", ["Dumaitic"] = "sem-dum", ["Dumbea"] = "duf", ["Dumi"] = "dus", ["Dumpas"] = "dmv", ["Dumun"] = "dui", ["Duna"] = "duc", ["Dungan"] = "dng", ["Dungmali"] = "raa", ["Dungra Bhil"] = "duh", ["Dungu"] = "dbv", ["Dupaningan Agta"] = "duo", ["Dura"] = "drq", ["Duri"] = "mvp", ["Duriankere"] = "dbn", ["Duruwa"] = "pci", ["Dusner"] = "dsn", ["Dusun Deyah"] = "dun", ["Dusun Malang"] = "duq", ["Dusun Witu"] = "duw", ["Dutch"] = "nl", ["Dutch Sign Language"] = "dse", ["Duun"] = "dux", ["Duupa"] = "dae", ["Duvle"] = "duv", ["Duwai"] = "dbp", ["Duwet"] = "gve", ["Dwang"] = "nnu", ["Dyaabugay"] = "dyy", ["Dyaberdyaber"] = "dyb", ["Dyan"] = "dya", ["Dyangadi"] = "dyn", ["Dyirbal"] = "dbl", ["Dyugun"] = "dyd", ["Dyula"] = "dyu", ["Dza"] = "jen", ["Dzala"] = "dzl", ["Dzando"] = "dzn", ["Dzao Min"] = "bpn", ["Dzodinka"] = "add", ["Dzongkha"] = "dz", ["Dzuun"] = "dnn", ["Dâw"] = "kwa", ["E"] = "eee", ["E'ma Buyang"] = "yzg", ["Early Assamese"] = "inc-oas", ["Early Modern Korean"] = "ko-ear", ["Early Old Oghuz"] = "trk-eog", ["Early Tripuri"] = "xtr", ["East Central German"] = "gmw-ecg", ["East Circassian"] = "kbd", ["East Damar"] = "dmr", ["East Franconian"] = "vmf", ["East Futuna"] = "fud", ["East Kewa"] = "kjs", ["East Limba"] = "lma", ["East Masela"] = "vme", ["East Miraya Bikol"] = "rbl", ["East Nyala"] = "nle", ["East Tarangan"] = "tre", ["East Yugur"] = "yuy", ["Eastern Acipa"] = "acp", ["Eastern Arrernte"] = "aer", ["Eastern Bolivian Guarani"] = "gui", ["Eastern Bontoc"] = "ebk", ["Eastern Bru"] = "bru", ["Eastern Canadian Inuktitut"] = "ike", ["Eastern Cham"] = "cjm", ["Eastern Durango Nahuatl"] = "azd", ["Eastern Gorkha Tamang"] = "tge", ["Eastern Highland Chatino"] = "cly", ["Eastern Highland Otomi"] = "otm", ["Eastern Huasteca Nahuatl"] = "nhe", ["Eastern Huishui Hmong"] = "hme", ["Eastern Karaboro"] = "xrb", ["Eastern Katu"] = "ktv", ["Eastern Kayah"] = "eky", ["Eastern Keres"] = "kee", ["Eastern Khanty"] = "kca-eas", ["Eastern Krahn"] = "kqo", ["Eastern Lalu"] = "yit", ["Eastern Lawa"] = "lwl", ["Eastern Magar"] = "mgp", ["Eastern Maninkakan"] = "emk", ["Eastern Mari"] = "mhr", ["Eastern Meohang"] = "emg", ["Eastern Min"] = "cdo", ["Eastern Mnong"] = "mng", ["Eastern Muria"] = "emu", ["Eastern Ngad'a"] = "nea", ["Eastern Nisu"] = "nos", ["Eastern Ojibwa"] = "ojg", ["Eastern Parbate Kham"] = "kif", ["Eastern Penan"] = "pez", ["Eastern Pomo"] = "peb", ["Eastern Pwo"] = "kjp", ["Eastern Qiandong Miao"] = "hmq", ["Eastern Subanun"] = "sfe", ["Eastern Tamang"] = "taj", ["Eastern Tawbuid"] = "bnj", ["Eastern Xiangxi Miao"] = "muq", ["Eastern Xwla Gbe"] = "gbx", ["Ebira"] = "igb", ["Eblaite"] = "xeb", ["Ebrié"] = "ebr", ["Ebughu"] = "ebg", ["Ecuadorian Sign Language"] = "ecs", ["Ede Cabe"] = "cbj", ["Ede Ica"] = "ica", ["Ede Idaca"] = "idd", ["Ede Ije"] = "ijj", ["Ede Nago"] = "nqg", ["Edera Awyu"] = "awy", ["Edo"] = "bin", ["Edolo"] = "etr", ["Edomite"] = "xdm", ["Edopi"] = "dbf", ["Efai"] = "efa", ["Efe"] = "efe", ["Efik"] = "efi", ["Efutop"] = "ofu", ["Ega"] = "ega", ["Eggon"] = "ego", ["Egyptian"] = "egy", ["Egyptian Arabic"] = "arz", ["Egyptian Sign Language"] = "esl", ["Ehueun"] = "ehu", ["Eipomek"] = "eip", ["Eitiep"] = "eit", ["Ejagham"] = "etu", ["Ejamat"] = "eja", ["Ekajuk"] = "eka", ["Ekari"] = "ekg", ["Ekele"] = "khy", ["Eki"] = "eki", ["Ekit"] = "eke", ["Ekpeye"] = "ekp", ["El Alto Zapotec"] = "zpp", ["El Hugeirat"] = "elh", ["El Molo"] = "elo", ["Elamite"] = "elx", ["Eleme"] = "elm", ["Elepi"] = "ele", ["Elfdalian"] = "ovd", ["Elip"] = "ekm", ["Elkei"] = "elk", ["Eloi"] = "art-elo", ["Elotepec Zapotec"] = "zte", ["Eloyi"] = "afo", ["Elseng"] = "mrf", ["Elu"] = "elu", ["Elymian"] = "xly", ["Emae"] = "mmw", ["Emai"] = "ema", ["Eman"] = "emn", ["Embaloh"] = "emb", ["Emberá-Baudó"] = "bdc", ["Emberá-Catío"] = "cto", ["Emberá-Chamí"] = "cmi", ["Emberá-Tadó"] = "tdc", ["Embu"] = "ebu", ["Emem"] = "enr", ["Emerillon"] = "eme", ["Emilian"] = "egl", ["Emplawas"] = "emw", ["En"] = "enc", ["Enawené-Nawé"] = "unk", ["Ende"] = "end", ["Enga"] = "enq", ["Engenni"] = "enn", ["Enggano"] = "eno", ["English"] = "en", ["Enlhet"] = "enl", ["Enrekang"] = "ptt", ["Enu"] = "enu", ["Enwan"] = "env", ["Enwang"] = "enw", ["Enxet"] = "enx", ["Enya"] = "gey", ["Eotile"] = "eot", ["Epena"] = "sja", ["Epi-Olmec"] = "xep", ["Epie"] = "epi", ["Epigraphic Mayan"] = "emy", ["Eravallan"] = "era", ["Erave"] = "kjy", ["Ere"] = "twp", ["Erie"] = "iro-ere", ["Eritai"] = "ert", ["Erokwanas"] = "erw", ["Erre"] = "err", ["Erromintxela"] = "emx", ["Ersu"] = "ers", ["Eruwa"] = "erh", ["Erzya"] = "myv", ["Esan"] = "ish", ["Ese"] = "mcq", ["Ese Ejja"] = "ese", ["Eshtehardi"] = "esh", ["Esimbi"] = "ags", ["Eskayan"] = "esy", ["Esmeralda"] = "sai-esm", ["Esperanto"] = "eo", ["Esselen"] = "esq", ["Estado de México Otomi"] = "ots", ["Estonian"] = "et", ["Estonian Sign Language"] = "eso", ["Esuma"] = "esm", ["Etchemin"] = "etc", ["Etebi"] = "etb", ["Eten"] = "etx", ["Eteocretan"] = "ecr", ["Eteocypriot"] = "ecy", ["Ethiopian Sign Language"] = "eth", ["Etkywan"] = "ich", ["Eton (Cameroon)"] = "eto", ["Eton (Vanuatu)"] = "etn", ["Etruscan"] = "ett", ["Etulo"] = "utr", ["Evant"] = "bzz", ["Even"] = "eve", ["Evenki"] = "evn", ["Ewage-Notu"] = "nou", ["Ewarhuyana"] = "sai-ewa", ["Ewe"] = "ee", ["Ewondo"] = "ewo", ["Extremaduran"] = "ext", ["Eyak"] = "eya", ["Ezaa"] = "eza", ["Fagani"] = "faf", ["Faifi"] = "fif", ["Faire Atta"] = "azt", ["Faiwol"] = "fai", ["Fakkanci"] = "gel", ["Fala"] = "fax", ["Falam Chin"] = "cfm", ["Fali"] = "fli", ["Faliscan"] = "xfa", ["Fam"] = "fam", ["Fanagalo"] = "fng", ["Fanamaket"] = "bjp", ["Fang (Bantu)"] = "fan", ["Fang (Beboid)"] = "fak", ["Fania"] = "fni", ["Far Western Muria"] = "fmu", ["Farefare"] = "gur", ["Faroese"] = "fo", ["Fas"] = "fqs", ["Fasu"] = "faa", ["Fataleka"] = "far", ["Fataluku"] = "ddg", ["Fayu"] = "fau", ["Fe'fe'"] = "fmp", ["Fedan"] = "pdn", ["Fembe"] = "agl", ["Fer"] = "kah", ["Feroge"] = "fer", ["Fiji Hindi"] = "hif", ["Fijian"] = "fj", ["Filomena Mata-Coahuitlán Totonac"] = "tlp", ["Fingallian"] = "gmw-fin", ["Finnish"] = "fi", ["Finnish Sign Language"] = "fse", ["Finnish-Swedish Sign Language"] = "fss", ["Finongan"] = "fag", ["Fipa"] = "fip", ["Firan"] = "fir", ["Fiwaga"] = "fiw", ["Flemish Sign Language"] = "vgt", ["Flinders Island"] = "fln", ["Foau"] = "flh", ["Fogaha"] = "ber-fog", ["Foi"] = "foi", ["Foia Foia"] = "ffi", ["Folopa"] = "ppo", ["Foma"] = "fom", ["Fon"] = "fon", ["Fongoro"] = "fgr", ["Foodo"] = "fod", ["Forak"] = "frq", ["Fordata"] = "frd", ["Fore"] = "for", ["Forest Enets"] = "enf", ["Forest Nenets"] = "yrk-for", ["Fortsenal"] = "frt", ["Fox"] = "sac", ["Franc-Comtois"] = "roa-fcm", ["Francisco León Zoque"] = "zos", ["Franco-Provençal"] = "frp", ["French"] = "fr", ["French Belgian Sign Language"] = "sfb", ["French Sign Language"] = "fsl", ["Friulian"] = "fur", ["Fula"] = "ff", ["Fuliiru"] = "flr", ["Fulniô"] = "fun", ["Fum"] = "fum", ["Fungwa"] = "ula", ["Fur"] = "fvr", ["Furu"] = "fuu", ["Futuna-Aniwa"] = "fut", ["Fuyug"] = "fuy", ["Fwe"] = "fwe", ["Fwâi"] = "fwa", ["Fyam"] = "pym", ["Fyer"] = "fie", ["Ga"] = "gaa", ["Ga'anda"] = "gqa", ["Ga'dang"] = "gdg", ["Gaa"] = "ttb", ["Gaam"] = "tbi", ["Gabadi"] = "kbt", ["Gabi"] = "gbw", ["Gabri"] = "gab", ["Gabrielino-Fernandeño"] = "xgf", ["Gadang"] = "gdk", ["Gaddang"] = "gad", ["Gaddi"] = "gbk", ["Gade"] = "ged", ["Gadjerawang"] = "gdh", ["Gadsup"] = "gaj", ["Gafat"] = "gft", ["Gagadu"] = "gbu", ["Gagauz"] = "gag", ["Gagnoa Bété"] = "btg", ["Gahri"] = "bfu", ["Gaikundi"] = "gbf", ["Gaina"] = "gcn", ["Gal"] = "gap", ["Galambu"] = "glo", ["Galatian"] = "xga", ["Galela"] = "gbi", ["Galeya"] = "gar", ["Galice"] = "gce", ["Galician"] = "gl", ["Galindian"] = "xgl", ["Gallaecian"] = "cel-gal", ["Gallo"] = "roa-gal", ["Gallo-Italic of Basilicata"] = "roa-gib", ["Gallo-Italic of Sicily"] = "roa-gis", ["Gallurese"] = "sdn", ["Galo"] = "adl", ["Galoli"] = "gal", ["Gamale Kham"] = "kgj", ["Gambera"] = "gma", ["Gamela"] = "sai-gam", ["Gamilaraay"] = "kld", ["Gamit"] = "gbl", ["Gamkonora"] = "gak", ["Gamo"] = "gmv", ["Gamo-Ningi"] = "bte", ["Gan"] = "gan", ["Gana"] = "gnq", ["Ganang"] = "gne", ["Gandhari"] = "pgd", ["Gane"] = "gzn", ["Ganggalida"] = "gcd", ["Ganglau"] = "ggl", ["Gangte"] = "gnb", ["Gangulu"] = "gnl", ["Gants"] = "gao", ["Ganza"] = "gza", ["Ganzi"] = "gnz", ["Gao"] = "gga", ["Gapapaiwa"] = "pwg", ["Garawa"] = "wrk", ["Garhwali"] = "gbm", ["Garifuna"] = "cab", ["Garingbal"] = "xgi", ["Garo"] = "grt", ["Garre"] = "gex", ["Garus"] = "gyb", ["Garza"] = "xgr", ["Gashowu Yokuts"] = "yok-gsy", ["Gata'"] = "gaq", ["Gaulish"] = "cel-gau", ["Gavak"] = "dmc", ["Gavar"] = "gou", ["Gavião do Jiparaná"] = "gvo", ["Gawar-Bati"] = "gwt", ["Gawwada"] = "gwd", ["Gaya"] = "zra", ["Gayil"] = "gyl", ["Gayo"] = "gay", ["Gayón"] = "sai-gay", ["Gbagyi"] = "gbr", ["Gban"] = "ggu", ["Gbanu"] = "gbv", ["Gbanziri"] = "gbg", ["Gbari"] = "gby", ["Gbaya-Bossangoa"] = "gbp", ["Gbaya-Bozoum"] = "gbq", ["Gbaya-Mbodomo"] = "gmm", ["Gbayi"] = "gyg", ["Gbesi Gbe"] = "gbs", ["Gbii"] = "ggb", ["Gbin"] = "xgb", ["Gbiri-Niragu"] = "grh", ["Gboloo Grebo"] = "gec", ["Gciriku"] = "diu", ["Gcwi"] = "gwj", ["Ge"] = "hmj", ["Ge'ez"] = "gez", ["Geba Karen"] = "kvq", ["Gebe"] = "gei", ["Gedaged"] = "gdd", ["Gedeo"] = "drs", ["Geji"] = "gji", ["Geko Karen"] = "ghk", ["Gela"] = "nlg", ["Gele'"] = "sbc", ["Geme"] = "geq", ["Gen"] = "gej", ["Gende"] = "gaf", ["Gengle"] = "geg", ["Georgian"] = "ka", ["Gepo"] = "ygp", ["Gera"] = "gew", ["Gerka"] = "gek", ["German"] = "de", ["German Sign Language"] = "gsg", ["Geruma"] = "gea", ["Geser-Gorom"] = "ges", ["Geshiza"] = "ero-gsz", ["Gey"] = "guv", ["Ghadames"] = "gha", ["Ghanaian Sign Language"] = "gse", ["Ghandruk Sign Language"] = "gds", ["Ghanongga"] = "ghn", ["Ghari"] = "gri", ["Ghayavi"] = "bmk", ["Ghera"] = "ghr", ["Ghomala'"] = "bbj", ["Ghomara"] = "gho", ["Ghotuo"] = "aaa", ["Ghulfan"] = "ghl", ["Giangan"] = "bgi", ["Gibanawa"] = "gib", ["Gidar"] = "gid", ["Gikyode"] = "acd", ["Gilaki"] = "glk", ["Gilbertese"] = "gil", ["Gilima"] = "gix", ["Gimi (Austronesian)"] = "gip", ["Gimi (Papuan)"] = "gim", ["Gimme"] = "kmp", ["Gimnime"] = "gmn", ["Ginuman"] = "gnm", ["Girawa"] = "bbr", ["Girirra"] = "gii", ["Giryama"] = "nyf", ["Githabul"] = "gih", ["Gitua"] = "ggt", ["Gitxsan"] = "git", ["Giyug"] = "giy", ["Gizrra"] = "tof", ["Glaro-Twabo"] = "glr", ["Glavda"] = "glw", ["Glio-Oubi"] = "oub", ["Glosa"] = "igs", ["Gnau"] = "gnu", ["Goa'uld"] = "art-gld", ["Goaria"] = "gig", ["Gobasi"] = "goi", ["Gobu"] = "gox", ["Godié"] = "god", ["Godoberi"] = "gdo", ["Godwari"] = "gdx", ["Goemai"] = "ank", ["Gofa"] = "gof", ["Gogo"] = "gog", ["Gogodala"] = "ggw", ["Goguryeo"] = "zkg", ["Gojri"] = "gju", ["Gokana"] = "gkn", ["Gokhy"] = "tbq-gkh", ["Gola"] = "gol", ["Golin"] = "gvf", ["Golpa"] = "lja", ["Gondi"] = "gon", ["Gone Dau"] = "goo", ["Gong"] = "ugo", ["Gongduk"] = "goe", ["Gonja"] = "gjn", ["Goo"] = "gov", ["Gooniyandi"] = "gni", ["Gor"] = "gqr", ["Gorakor"] = "goc", ["Gorap"] = "goq", ["Goreng"] = "xgg", ["Gorontalo"] = "gor", ["Gorovu"] = "grq", ["Gorwaa"] = "gow", ["Gothic"] = "got", ["Gottscheerish"] = "gmw-gts", ["Goundo"] = "goy", ["Gourmanchéma"] = "gux", ["Gowlan"] = "goj", ["Gowro"] = "gwf", ["Gozarkhani"] = "goz", ["Grangali"] = "nli", ["Grass Koiari"] = "kbk", ["Greek"] = "el", ["Greek Sign Language"] = "gss", ["Green Gelao"] = "giq", ["Green Hmong"] = "hnj", ["Greenlandic"] = "kl", ["Grenadian Creole English"] = "gcl", ["Gresi"] = "grs", ["Groma"] = "gro", ["Gros Ventre"] = "ats", ["Gua"] = "gwx", ["Guachí"] = "sai-gua", ["Guahibo"] = "guh", ["Guajajára"] = "gub", ["Guajá"] = "gvj", ["Guambiano"] = "gum", ["Guamo"] = "sai-gmo", ["Guanano"] = "gvc", ["Guanche"] = "gnc", ["Guarayu"] = "gyr", ["Guatemalan Sign Language"] = "gsm", ["Guató"] = "gta", ["Guayabero"] = "guo", ["Guazacapán"] = "nai-guz", ["Gudang"] = "xgd", ["Gudanji"] = "nji", ["Gude"] = "gde", ["Gudu"] = "gdu", ["Guduf-Gava"] = "gdf", ["Guerrero Amuzgo"] = "amu", ["Guerrero Nahuatl"] = "ngu", ["Guevea de Humboldt Zapotec"] = "zpg", ["Gugadj"] = "ggd", ["Gugu Badhun"] = "gdc", ["Gugu Warra"] = "wrw", ["Guhu-Samane"] = "ghs", ["Guianese Creole"] = "gcr", ["Guiberoua Bété"] = "bet", ["Guinau"] = "awd-gnu", ["Guinea Kpelle"] = "gkp", ["Guinea-Bissau Creole"] = "pov", ["Guinea-Bissau Sign Language"] = "lgs", ["Guinean Sign Language"] = "gus", ["Guiqiong"] = "gqi", ["Gujarati"] = "gu", ["Gula"] = "glu", ["Gula'alaa"] = "gmb", ["Gulay"] = "gvl", ["Gule"] = "gly", ["Gulf Arabic"] = "afb", ["Gullah"] = "gul", ["Gumalu"] = "gmu", ["Gumatj"] = "gnn", ["Gumawana"] = "gvs", ["Gumuz"] = "guk", ["Gun"] = "guw", ["Gundi"] = "gdi", ["Gunditjmara"] = "gjm", ["Gundungurra"] = "xrd", ["Gungabula"] = "gyf", ["Gungu"] = "rub", ["Guntai"] = "gnt", ["Gunu"] = "yas", ["Gunwinggu"] = "gup", ["Gunya"] = "gyy", ["Gupa-Abawa"] = "gpa", ["Gupapuyngu"] = "guf", ["Gur Lama"] = "las", ["Guragone"] = "gge", ["Guramalum"] = "grz", ["Gurani"] = "hac", ["Gureng Gureng"] = "gnr", ["Gurgula"] = "ggg", ["Guriaso"] = "grx", ["Gurindji"] = "gue", ["Gurindji Kriol"] = "gjr", ["Gurmana"] = "gvm", ["Guro"] = "goa", ["Gurung"] = "gvr", ["Guruntum"] = "grd", ["Gusan"] = "gsn", ["Gusii"] = "guz", ["Gusilay"] = "gsl", ["Gutnish"] = "gmq-gut", ["Guugu Yimidhirr"] = "kky", ["Guwa"] = "xgw", ["Guwamu"] = "gwu", ["Guwar"] = "aus-guw", ["Guya"] = "gka", ["Guyanese Creole English"] = "gyn", ["Guyani"] = "gvy", ["Guébie"] = "gie", ["Gvoko"] = "ngs", ["Gwa"] = "gwb", ["Gwahatike"] = "dah", ["Gwak"] = "jgk", ["Gwamhi-Wuri"] = "bga", ["Gwandara"] = "gwn", ["Gwara"] = "alv-gwa", ["Gweda"] = "grw", ["Gweno"] = "gwe", ["Gwere"] = "gwr", ["Gwich'in"] = "gwi", ["Gyalsumdo"] = "gyo", ["Gyele"] = "gyi", ["Gyem"] = "gye", ["Güenoa"] = "sai-gue", ["Habu"] = "hbu", ["Hachijō"] = "jpx-hcj", ["Hadiyya"] = "hdy", ["Hadoti"] = "hoj", ["Hadrami"] = "xhd", ["Hadza"] = "hts", ["Haeke"] = "aek", ["Hahon"] = "hah", ["Haida"] = "hai", ["Haigwai"] = "hgw", ["Hailufeng Min"] = "nan-hlh", ["Hainanese"] = "hnm", ["Hainyaxo Bozo"] = "bzx", ["Haiphong Sign Language"] = "haf", ["Haisla"] = "has", ["Haitian Creole"] = "ht", ["Haitian Vodoun Culture Language"] = "hvc", ["Haiǁom"] = "hgm", ["Haji"] = "hji", ["Hajong"] = "haj", ["Hakka"] = "hak", ["Hakö"] = "hao", ["Halang"] = "hal", ["Halang Doan"] = "hld", ["Halbi"] = "hlb", ["Halia"] = "hla", ["Halkomelem"] = "hur", ["Hamap"] = "hmu", ["Hamba"] = "hba", ["Hamer-Banna"] = "amf", ["Hamtai"] = "hmt", ["Hanga"] = "hag", ["Hanga Hundi"] = "wos", ["Hani"] = "hni", ["Hanoi Sign Language"] = "hab", ["Hanunoo"] = "hnn", ["Harami"] = "xha", ["Harappan"] = "xiv", ["Harari"] = "har", ["Haraza"] = "nub-har", ["Harijan Kinnauri"] = "kjo", ["Haroi"] = "hro", ["Harsusi"] = "hss", ["Haruai"] = "tmd", ["Haruku"] = "hrk", ["Haryanvi"] = "bgc", ["Harzani"] = "hrz", ["Hasaitic"] = "sem-has", ["Hasha"] = "ybj", ["Hassaniya Arabic"] = "mey", ["Hatam"] = "had", ["Hattic"] = "xht", ["Hausa"] = "ha", ["Hausa Sign Language"] = "hsl", ["Haush"] = "sai-hau", ["Havasupai-Walapai-Yavapai"] = "yuf", ["Haveke"] = "hvk", ["Havu"] = "hav", ["Hawai'i Pidgin Sign Language"] = "hps", ["Hawaiian"] = "haw", ["Hawaiian Creole"] = "hwc", ["Haya"] = "hay", ["Hdi"] = "xed", ["Hebrew"] = "he", ["Hehe"] = "heh", ["Heiban"] = "hbn", ["Heiltsuk"] = "hei", ["Helong"] = "heg", ["Hema"] = "nix", ["Hemba"] = "hem", ["Hember Avu"] = "mmi", ["Herdé"] = "hed", ["Herero"] = "hz", ["Hermit"] = "llf", ["Hernican"] = "xhr", ["Hewa"] = "ham", ["Heyo"] = "auk", ["Hibito"] = "hib", ["Hidatsa"] = "hid", ["Higaonon"] = "mba", ["High Valyrian"] = "art-vlh", ["Highland Konjo"] = "kjk", ["Highland Oaxaca Chontal"] = "chd", ["Highland Popoluca"] = "poi", ["Highland Puebla Nahuatl"] = "azz", ["Highland Totonac"] = "tos", ["Hijazi Arabic"] = "acw", ["Hijuk"] = "hij", ["Hiligaynon"] = "hil", ["Hill Maria"] = "mrr", ["Himarimã"] = "hir", ["Hindi"] = "hi", ["Hinduri"] = "hii", ["Hinukh"] = "gin", ["Hiri Motu"] = "ho", ["Hismaic"] = "sem-his", ["Hitchiti"] = "nai-hit", ["Hittite"] = "hit", ["Hitu"] = "htu", ["Hiw"] = "hiw", ["Hixkaryana"] = "hix", ["Hlai"] = "lic", ["Hlepho Phowa"] = "yhl", ["Hlersu"] = "hle", ["Hmar"] = "hmr", ["Hmong Don"] = "hmf", ["Hmong Dô"] = "hmv", ["Hmong Shua"] = "hmz", ["Hmwaveke"] = "mrk", ["Ho"] = "hoc", ["Ho Chi Minh City Sign Language"] = "hos", ["Hoava"] = "hoa", ["Hobyót"] = "hoh", ["Hoia Hoia"] = "hhi", ["Hokkien"] = "nan-hbl", ["Holikachuk"] = "hoi", ["Holiya"] = "hoy", ["Holma"] = "hod", ["Holoholo"] = "hoo", ["Holu"] = "hol", ["Homa"] = "hom", ["Honduran Lenca"] = "len", ["Honduras Sign Language"] = "hds", ["Hone"] = "juh", ["Hong Kong Sign Language"] = "hks", ["Honi"] = "how", ["Hopi"] = "hop", ["Horned Miao"] = "hrm", ["Horo"] = "hor", ["Horom"] = "hoe", ["Hote"] = "hot", ["Hoti"] = "hti", ["Hovongan"] = "hov", ["Hoyahoya"] = "hhy", ["Hozo"] = "hoz", ["Hpon"] = "hpo", ["Hrangkhol"] = "hra", ["Hruso"] = "hru", ["Hrê"] = "hre", ["Hu"] = "huo", ["Huachipaeri"] = "hug", ["Huambisa"] = "hub", ["Huaorani"] = "auc", ["Huarijio"] = "var", ["Huaulu"] = "hud", ["Huautla Mazatec"] = "mau", ["Huave"] = "huv", ["Huaxcaleca Nahuatl"] = "nhq", ["Huba"] = "hbb", ["Huehuetla Tepehua"] = "tee", ["Huetar"] = "cba-hue", ["Huichol"] = "hch", ["Huilliche"] = "huh", ["Huitepec Mixtec"] = "mxs", ["Huizhou"] = "czh", ["Hukumina"] = "huw", ["Hula"] = "hul", ["Hulaulá"] = "huy", ["Huli"] = "hui", ["Hulung"] = "huk", ["Humburi Senni"] = "hmb", ["Humene"] = "huf", ["Hun"] = "uth", ["Hunde"] = "hke", ["Hung"] = "hnu", ["Hungana"] = "hum", ["Hungarian"] = "hu", ["Hungarian Sign Language"] = "hsh", ["Hungworo"] = "nat", ["Hunjara-Kaina Ke"] = "hkk", ["Hunnic"] = "xhc", ["Hunsrik"] = "hrx", ["Hunzib"] = "huz", ["Hupa"] = "hup", ["Hupdë"] = "jup", ["Hupla"] = "hap", ["Hurrian"] = "xhu", ["Hutterisch"] = "geh", ["Hwana"] = "hwo", ["Hya"] = "hya", ["Hyam"] = "jab", ["Hän"] = "haa", ["Hértevin"] = "hrt", ["I-Wak"] = "iwk", ["Iaai"] = "iai", ["Iamalele"] = "yml", ["Iatmul"] = "ian", ["Iau"] = "tmu", ["Ibali Teke"] = "tek", ["Ibaloi"] = "ibl", ["Iban"] = "iba", ["Ibanag"] = "ibg", ["Ibani"] = "iby", ["Ibatan"] = "ivb", ["Iberian"] = "xib", ["Ibibio"] = "ibb", ["Ibino"] = "ibn", ["Iboko"] = "bkp", ["Ibu"] = "ibu", ["Ibuoro"] = "ibr", ["Icelandic"] = "is", ["Icelandic Sign Language"] = "icl", ["Iceve-Maci"] = "bec", ["Ida'an"] = "dbj", ["Idakho-Isukha-Tiriki"] = "ida", ["Idaté"] = "idt", ["Idere"] = "ide", ["Idesa"] = "ids", ["Idi"] = "idi", ["Idiom Neutral"] = "mis-idn", ["Ido"] = "io", ["Idoma"] = "idu", ["Idon"] = "idc", ["Idu"] = "clk", ["Idun"] = "ldb", ["Iduna"] = "viv", ["Ifo"] = "iff", ["Ifè"] = "ife", ["Igala"] = "igl", ["Igana"] = "igg", ["Igbo"] = "ig", ["Igede"] = "ige", ["Ignaciano"] = "ign", ["Igo"] = "ahl", ["Iguta"] = "nar", ["Igwe"] = "igw", ["Iha"] = "ihp", ["Ihievbe"] = "ihi", ["Ija-Zuba"] = "vki", ["Ik"] = "ikx", ["Ika"] = "ikk", ["Ikaranggal"] = "ikr", ["Ikizu"] = "ikz", ["Iko"] = "iki", ["Ikobi-Mena"] = "meb", ["Ikoma"] = "ntk", ["Ikpeng"] = "txi", ["Ikpeshi"] = "ikp", ["Ikposo"] = "kpo", ["Iku-Gora-Ankwa"] = "ikv", ["Ikulu"] = "ikl", ["Ikwere"] = "ikw", ["Ikwo"] = "iqw", ["Ila"] = "ilb", ["Ile Ape"] = "ila", ["Ilgar"] = "ilg", ["Ili Turki"] = "ili", ["Ili'uun"] = "ilu", ["Ilianen Manobo"] = "mbi", ["Illyrian"] = "xil", ["Ilocano"] = "ilo", ["Ilongot"] = "ilk", ["Ilue"] = "ilv", ["Ilwana"] = "mlk", ["Imbongu"] = "imo", ["Imonda"] = "imn", ["Imroing"] = "imr", ["Inabaknon"] = "abx", ["Inapang"] = "mzu", ["Inari Sami"] = "smn", ["Indanga"] = "bnt-ind", ["Indian Sign Language"] = "ins", ["Indo-Portuguese"] = "idb", ["Indonesian"] = "id", ["Indonesian Bajau"] = "bdl", ["Indonesian Sign Language"] = "inl", ["Indri"] = "idr", ["Indus Kohistani"] = "mvy", ["Inebu One"] = "oin", ["Ineseño"] = "inz", ["Inga"] = "inb", ["Ingrian"] = "izh", ["Ingush"] = "inh", ["Inlaod Itneg"] = "iti", ["Inoke-Yate"] = "ino", ["Inonhan"] = "loc", ["Inor"] = "ior", ["Inpui Naga"] = "nkf", ["Interlingua"] = "ia", ["Interlingue"] = "ie", ["International Sign"] = "ils", ["Interslavic"] = "isv", ["Intha"] = "int", ["Inuinnaqtun"] = "esx-inq", ["Inuit Sign Language"] = "iks", ["Inuktitut"] = "iu", ["Inuktun"] = "esx-ink", ["Inupiaq"] = "ik", ["Inuvialuktun"] = "ikt", ["Ipai"] = "nai-ipa", ["Ipalapa Amuzgo"] = "azm", ["Ipiko"] = "ipo", ["Ipili"] = "ipi", ["Ipulo"] = "ass", ["Iquito"] = "iqu", ["Ir"] = "irr", ["Irantxe"] = "irn", ["Iranun"] = "ill", ["Iraqi Arabic"] = "acm", ["Iraqw"] = "irk", ["Irarutu"] = "irh", ["Iraya"] = "iry", ["Iresim"] = "ire", ["Irish"] = "ga", ["Irish Sign Language"] = "isg", ["Irula"] = "iru", ["Isabi"] = "isa", ["Isan"] = "tts", ["Isanzu"] = "isn", ["Isarog Agta"] = "agk", ["Isaurian"] = "mis-isa", ["Isconahua"] = "isc", ["Isebe"] = "igo", ["Ishkashimi"] = "isk", ["Isinai"] = "inn", ["Isirawa"] = "srl", ["Isnag"] = "isd", ["Isoko"] = "iso", ["Israeli Sign Language"] = "isr", ["Isthmus Mixe"] = "mir", ["Isthmus Zapotec"] = "zai", ["Istriot"] = "ist", ["Istro-Romanian"] = "ruo", ["Isu"] = "isu", ["Isubu"] = "szv", ["Italian"] = "it", ["Italian Sign Language"] = "ise", ["Italiot Greek"] = "grk-ita", ["Itawit"] = "itv", ["Itelmen"] = "itl", ["Itene"] = "ite", ["Iteri"] = "itr", ["Itik"] = "itx", ["Ito"] = "itw", ["Itonama"] = "ito", ["Itsekiri"] = "its", ["Itu Mbon Uzo"] = "itm", ["Itundujia Mixtec"] = "mce", ["Itza'"] = "itz", ["Iu Mien"] = "ium", ["Ivatan"] = "ivv", ["Iwaidja"] = "ibd", ["Iwal"] = "kbm", ["Iwam"] = "iwm", ["Iwur"] = "iwo", ["Ixcatec"] = "ixc", ["Ixcatlán Mazatec"] = "mzi", ["Ixil"] = "ixl", ["Ixtayutla Mixtec"] = "vmj", ["Ixtenco Otomi"] = "otz", ["Iyayu"] = "iya", ["Iyive"] = "uiv", ["Iyo"] = "nca", ["Iyo'wujwa Chorote"] = "crq", ["Iyojwa'ja Chorote"] = "crt", ["Izere"] = "izr", ["Izi"] = "izz", ["Izi-Ezaa-Ikwo-Mgbo"] = "izi", ["Izon"] = "ijc", ["Izora"] = "cbo", ["Iñapari"] = "inp", ["Jabem"] = "jae", ["Jabutí"] = "jbt", ["Jad"] = "jda", ["Jadgali"] = "jdg", ["Jah Hut"] = "jah", ["Jahanka"] = "jad", ["Jair Awyu"] = "awv", ["Jakaltek"] = "jac", ["Jakati"] = "jat", ["Jalapa de Díaz Mazatec"] = "maj", ["Jalkunan"] = "bxl", ["Jamaican Country Sign Language"] = "jcs", ["Jamaican Creole"] = "jam", ["Jamaican Sign Language"] = "jls", ["Jamamadí"] = "jaa", ["Jambi Malay"] = "jax", ["Jamiltepec Mixtec"] = "mxt", ["Jaminjung"] = "djd", ["Jamsay"] = "djm", ["Jamtish"] = "gmq-jmk", ["Jandavra"] = "jnd", ["Janday"] = "jan", ["Jangkang"] = "djo", ["Jangshung"] = "jna", ["Janji"] = "jni", ["Japanese"] = "ja", ["Japanese Sign Language"] = "jsl", ["Japhug"] = "sit-jap", ["Japrería"] = "jru", ["Jaqaru"] = "jqr", ["Jara"] = "jaf", ["Jarai"] = "jra", ["Jarawa"] = "anq", ["Jaru"] = "ddj", ["Jassic"] = "ysc", ["Jaunsari"] = "jns", ["Javanese"] = "jv", ["Javindo"] = "jvd", ["Jawe"] = "jaz", ["Jaya"] = "jyy", ["Jebero"] = "jeb", ["Jeh"] = "jeh", ["Jehai"] = "jhi", ["Jeikó"] = "sai-jko", ["Jeju"] = "jje", ["Jemez"] = "tow", ["Jenaama Bozo"] = "bze", ["Jeng"] = "jeg", ["Jennu Kurumba"] = "xuj", ["Jere"] = "jer", ["Jeri Kuo"] = "jek", ["Jersey Dutch"] = "gmw-jdt", ["Jeru"] = "akj", ["Jerung"] = "jee", ["Jhankot Sign Language"] = "jhs", ["Jiamao"] = "jio", ["Jiba"] = "juo", ["Jibu"] = "jib", ["Jicarilla"] = "apj", ["Jie"] = "mis-jie", ["Jiiddu"] = "jii", ["Jilbe"] = "jie", ["Jili"] = "mgi", ["Jilim"] = "jil", ["Jimi"] = "jmi", ["Jimjimen"] = "jim", ["Jin"] = "cjy", ["Jina"] = "jia", ["Jingpho"] = "kac", ["Jingulu"] = "jig", ["Jiongnai Bunu"] = "pnu", ["Jirajara"] = "sai-jrj", ["Jirel"] = "jul", ["Jiru"] = "jrr", ["Jita"] = "jit", ["Jizhao"] = "mis-jzh", ["Jju"] = "kaj", ["Joba"] = "job", ["Jofotek-Bromnya"] = "jbr", ["Jola-Fonyi"] = "dyo", ["Jola-Kasa"] = "csk", ["Jonkor Bourmataguil"] = "jeu", ["Jordanian Sign Language"] = "jos", ["Jorá"] = "jor", ["Jowulu"] = "jow", ["Ju"] = "juu", ["Juang"] = "jun", ["Juba Arabic"] = "pga", ["Judeo-Italian"] = "itk", ["Judeo-Persian"] = "jpr", ["Judeo-Tat"] = "jdt", ["Jukun Takum"] = "jbu", ["Jumaytepeque"] = "nai-jum", ["Jumjum"] = "jum", ["Jumla Sign Language"] = "jus", ["Jumli"] = "jml", ["Jungle Inga"] = "inj", ["Juquila Mixe"] = "mxq", ["Jur Modo"] = "bex", ["Juray"] = "juy", ["Jurchen"] = "juc", ["Jurúna"] = "jur", ["Jutiapa"] = "nai-jtp", ["Jutish"] = "jut", ["Juwal"] = "mwb", ["Juxtlahuaca Mixtec"] = "vmc", ["Juǀ'hoan"] = "ktz", ["Jwira-Pepesa"] = "jwi", ["Júma"] = "jua", ["K'iche'"] = "quc", ["Kaamba"] = "xku", ["Kaan"] = "ldl", ["Kaang Chin"] = "ckn", ["Kaansa"] = "gna", ["Kaapor Sign Language"] = "uks", ["Kaba"] = "ksp", ["Kabalai"] = "kvf", ["Kabatei"] = "xkp", ["Kabba-Laka"] = "lap", ["Kabishiana"] = "tup-kab", ["Kabiye"] = "kbp", ["Kabola"] = "klz", ["Kabore One"] = "onk", ["Kabras"] = "lkb", ["Kaburi"] = "uka", ["Kabutra"] = "kbu", ["Kabuverdianu"] = "kea", ["Kabwa"] = "cwa", ["Kabwari"] = "kcw", ["Kabyle"] = "kab", ["Kachama-Ganjule"] = "kcx", ["Kachari"] = "xac", ["Kachchi"] = "kfr", ["Kachi Koli"] = "gjk", ["Kacipo-Balesi"] = "koe", ["Kaco'"] = "xkk", ["Kadai"] = "kzd", ["Kadar"] = "kej", ["Kadara"] = "kad", ["Kadaru"] = "kdu", ["Kadiwéu"] = "kbc", ["Kado"] = "kdv", ["Kadu (Myanmar)"] = "zkd", ["Kadugli"] = "xtc", ["Kaduo"] = "ktp", ["Kaera"] = "jka", ["Kafa"] = "kbr", ["Kafoa"] = "kpu", ["Kagan Kalagan"] = "kll", ["Kagate"] = "syw", ["Kagayanen"] = "cgc", ["Kagoma"] = "kdm", ["Kagoro"] = "xkg", ["Kagulu"] = "kki", ["Kahe"] = "hka", ["Kahua"] = "agw", ["Kaian"] = "kct", ["Kaibobo"] = "kzb", ["Kaidipang"] = "kzp", ["Kaiep"] = "kbw", ["Kaikadi"] = "kep", ["Kaike"] = "kzq", ["Kaiku"] = "kkq", ["Kaimbulawa"] = "zka", ["Kaimbé"] = "xai", ["Kaingang"] = "kgp", ["Kairak"] = "ckr", ["Kairiru"] = "kxa", ["Kairui-Midiki"] = "krd", ["Kais"] = "kzm", ["Kaitag"] = "xdq", ["Kaivi"] = "kce", ["Kaiwá"] = "kgk", ["Kaiy"] = "tcq", ["Kajakse"] = "ckq", ["Kajali"] = "xkj", ["Kajaman"] = "kag", ["Kakabai"] = "kqf", ["Kakabe"] = "kke", ["Kakanda"] = "kka", ["Kaki Ae"] = "tbd", ["Kakihum"] = "kxe", ["Kako"] = "kkj", ["Kakwa"] = "keo", ["Kala"] = "kcl", ["Kala Lagaw Ya"] = "mwp", ["Kalaamaya"] = "lkm", ["Kalabakan"] = "kve", ["Kalabari"] = "ijn", ["Kalabra"] = "kzz", ["Kalagan"] = "kqe", ["Kalaktang Monpa"] = "kkf", ["Kalam"] = "kmh", ["Kalami"] = "gwc", ["Kalamsé"] = "knz", ["Kalanadi"] = "wkl", ["Kalanga"] = "kck", ["Kalao"] = "kly", ["Kalapuya"] = "kyl", ["Kalarko"] = "kba", ["Kalasha"] = "kls", ["Kalasuri"] = "xme-kls", ["Kalašma"] = "ine-kal", ["Kalenjin"] = "kln", ["Kalinago"] = "crb", ["Kalkatungu"] = "ktg", ["Kalkoti"] = "xka", ["Kallawaya"] = "caw", ["Kalmyk"] = "xal", ["Kalo Finnish Romani"] = "rmf", ["Kalou"] = "ywa", ["Kaluli"] = "bco", ["Kalumpang"] = "kli", ["Kam"] = "kdx", ["Kamakan"] = "vkm", ["Kamang"] = "woi", ["Kamano"] = "kbq", ["Kamantan"] = "kci", ["Kamar"] = "keq", ["Kamara"] = "jmr", ["Kamarian"] = "kzx", ["Kamaru"] = "kgx", ["Kamarupi Prakrit"] = "inc-kam", ["Kamasa"] = "klp", ["Kamasau"] = "kms", ["Kamassian"] = "xas", ["Kamayo"] = "kyk", ["Kamayurá"] = "kay", ["Kamba"] = "kam", ["Kambaata"] = "ktb", ["Kambaira"] = "kyy", ["Kambera"] = "xbr", ["Kamberataro"] = "kbv", ["Kamberau"] = "irx", ["Kambiwá"] = "xbw", ["Kami"] = "kmi", ["Kamkata-viri"] = "bsh", ["Kamo"] = "kcq", ["Kamoro"] = "kgq", ["Kamta"] = "rkt", ["Kamu"] = "xmu", ["Kamula"] = "xla", ["Kamwe"] = "hig", ["Kanakanabu"] = "xnb", ["Kanakuru"] = "kna", ["Kanamari"] = "knm", ["Kanashi"] = "xns", ["Kanasi"] = "soq", ["Kandas"] = "kqw", ["Kandawo"] = "gam", ["Kande"] = "kbs", ["Kang"] = "kyp", ["Kanga"] = "kcp", ["Kangean"] = "kkv", ["Kanggape"] = "igm", ["Kangjia"] = "kxs", ["Kango"] = "kty", ["Kango-Sua"] = "kzy", ["Kangri"] = "xnr", ["Kaniet"] = "ktk", ["Kanikkaran"] = "kev", ["Kaningdon-Nindem"] = "kdp", ["Kaningi"] = "kzo", ["Kaningra"] = "knr", ["Kaninuwa"] = "wat", ["Kanite"] = "kmu", ["Kanjari"] = "kft", ["Kanju"] = "kbe", ["Kankanaey"] = "kne", ["Kannada"] = "kn", ["Kannada Kurumba"] = "kfi", ["Kannauji"] = "bjj", ["Kanowit"] = "kxn", ["Kanoé"] = "kxo", ["Kansa"] = "ksk", ["Kantosi"] = "xkt", ["Kanu"] = "khx", ["Kanufi"] = "kni", ["Kanuri"] = "kr", ["Kanyok"] = "kny", ["Kao"] = "kax", ["Kaonde"] = "kqn", ["Kap"] = "ykm", ["Kapampangan"] = "pam", ["Kapauri"] = "khp", ["Kapin"] = "tbx", ["Kapinawá"] = "xpn", ["Kapingamarangi"] = "kpg", ["Kapriman"] = "dju", ["Kaptiau"] = "kbi", ["Kapya"] = "klo", ["Kaqchikel"] = "cak", ["Kaqchikel-K'iche' Mixed Language"] = "ckz", ["Kara (New Guinea)"] = "leu", ["Kara (Tanzania)"] = "reg", ["Karachay-Balkar"] = "krc", ["Karadjeri"] = "gbd", ["Karaga Mandaya"] = "mry", ["Karaim"] = "kdr", ["Karajá"] = "kpj", ["Karakalpak"] = "kaa", ["Karakhanid"] = "xqa", ["Karami"] = "xar", ["Karamojong"] = "kdj", ["Karang"] = "kzr", ["Karanga"] = "kth", ["Karankawa"] = "zkk", ["Karao"] = "kyj", ["Karas"] = "kgv", ["Karata"] = "kpt", ["Karawa"] = "xrw", ["Karbi"] = "mjw", ["Kare (Central Africa)"] = "kbn", ["Kare (New Guinea)"] = "kmf", ["Karekare"] = "kai", ["Karelian"] = "krl", ["Karey"] = "kyd", ["Kari"] = "kbj", ["Kari'na"] = "car", ["Karian"] = "bql", ["Karingani"] = "kgn", ["Karipuna"] = "kuq", ["Karipuna (Panoan)"] = "sai-krp", ["Karipúna Creole French"] = "kmv", ["Kariri"] = "kzw", ["Karitiâna"] = "ktn", ["Kariya"] = "kil", ["Kariyarra"] = "vka", ["Karkar-Yuri"] = "yuj", ["Karkin"] = "krb", ["Karko"] = "kko", ["Karnai"] = "bbv", ["Karo"] = "kxh", ["Karo Batak"] = "btx", ["Karok"] = "kyh", ["Karolanos"] = "kyn", ["Karon"] = "krx", ["Karon Dori"] = "kgw", ["Karore"] = "xkx", ["Karranga"] = "xrq", ["Karuwali"] = "rxw", ["Kasanga"] = "ccj", ["Kasem"] = "xsm", ["Kashaya"] = "kju", ["Kashmiri"] = "ks", ["Kashubian"] = "csb", ["Kasiguranin"] = "ksn", ["Kaska"] = "kkz", ["Kaskean"] = "zsk", ["Kaskihá"] = "gva", ["Kassite"] = "mis-kas", ["Kassonke"] = "kao", ["Kasua"] = "khs", ["Kataang"] = "kgd", ["Katabaga"] = "ktq", ["Katawixi"] = "xat", ["Katembri"] = "sai-kat", ["Kathlamet"] = "nai-kat", ["Kathoriya Tharu"] = "tkt", ["Kathu"] = "ykt", ["Katkari"] = "kfu", ["Katla"] = "kcr", ["Kato"] = "ktw", ["Katso"] = "kaf", ["Katua"] = "kta", ["Katukina"] = "knt", ["Kaulong"] = "pss", ["Kaur"] = "vkk", ["Kaure"] = "bpp", ["Kaurna"] = "zku", ["Kauwera"] = "xau", ["Kavalan"] = "ckv", ["Kavet"] = "krv", ["Kawacha"] = "kcb", ["Kawahiva"] = "tup-kaw", ["Kawaiisu"] = "xaw", ["Kawe"] = "kgb", ["Kawishana"] = "awd-kaw", ["Kawésqar"] = "alc", ["Kaxararí"] = "ktx", ["Kaxuyana"] = "kbb", ["Kayabí"] = "kyz", ["Kayagar"] = "kyt", ["Kayan"] = "pdu", ["Kayan Mahakam"] = "xay", ["Kayan River Kayan"] = "xkn", ["Kayapa Kallahan"] = "kak", ["Kayapó"] = "txu", ["Kayardild"] = "gyd", ["Kayeli"] = "kzl", ["Kayong"] = "kxy", ["Kayort"] = "kyv", ["Kaytetye"] = "gbb", ["Kayupulau"] = "kzu", ["Kazakh"] = "kk", ["Kazukuru"] = "kzk", ["Ke'o"] = "xxk", ["Keak"] = "keh", ["Keapara"] = "khz", ["Kedah Malay"] = "meo", ["Kedang"] = "ksx", ["Keder"] = "kdy", ["Kehu"] = "khh", ["Kei"] = "kei", ["Keiga"] = "kec", ["Kein"] = "bmh", ["Keiyo"] = "eyo", ["Kela-Yela"] = "kel", ["Kelabit"] = "kzi", ["Kelantan Peranakan Hokkien"] = "mis-hkl", ["Keley-I Kallahan"] = "ify", ["Keliko"] = "kbo", ["Kelo"] = "xel", ["Kelon"] = "kyo", ["Kemak"] = "kem", ["Kembayan"] = "xem", ["Kemberano"] = "bzp", ["Kembra"] = "xkw", ["Kemezung"] = "dmo", ["Kemi Sami"] = "sjk", ["Kemiehua"] = "kfj", ["Kemtuik"] = "kmt", ["Kenaboi"] = "xbn", ["Kenati"] = "gat", ["Kendayan"] = "knx", ["Kendeje"] = "klf", ["Kendem"] = "kvm", ["Kenga"] = "kyq", ["Keningau Murut"] = "kxi", ["Keninjal"] = "knl", ["Kensiu"] = "kns", ["Kenswei Nsei"] = "ndb", ["Kenyan Sign Language"] = "xki", ["Kenyang"] = "ken", ["Kenyi"] = "lke", ["Keoru-Ahia"] = "xeu", ["Kepkiriwát"] = "kpn", ["Kepo'"] = "kuk", ["Kera"] = "ker", ["Kerak"] = "hhr", ["Kereho"] = "xke", ["Kerek"] = "krk", ["Kerewe"] = "ked", ["Kerewo"] = "kxz", ["Kerinci"] = "kvr", ["Kermanic"] = "xme-ker", ["Ket"] = "ket", ["Ketangalan"] = "kae", ["Kete"] = "kcv", ["Ketengban"] = "xte", ["Ketum"] = "ktt", ["Kewa"] = "kew", ["Keyagana"] = "kyg", ["Kgalagadi"] = "xkv", ["Khakas"] = "kjh", ["Khalaj"] = "klj", ["Khaling"] = "klr", ["Khamnigan Mongol"] = "ykh", ["Khamti"] = "kht", ["Khamyang"] = "ksu", ["Khana"] = "ogo", ["Khao"] = "xao", ["Kharam Naga"] = "kfw", ["Kharia"] = "khr", ["Kharia Thar"] = "ksy", ["Khasi"] = "kha", ["Khayo"] = "lko", ["Khazar"] = "zkz", ["Khe"] = "kqg", ["Khehek"] = "tlx", ["Khengkha"] = "xkf", ["Khetrani"] = "xhe", ["Khezha Naga"] = "nkh", ["Khiamniungan Naga"] = "kix", ["Khinalug"] = "kjj", ["Khirwar"] = "kwx", ["Khisa"] = "kqm", ["Khitan"] = "zkt", ["Khlula"] = "ykl", ["Khmer"] = "km", ["Khmu"] = "kjg", ["Khoekhoe"] = "naq", ["Khoibu Naga"] = "nkb", ["Khoini"] = "xkc", ["Kholok"] = "ktc", ["Kholosi"] = "inc-kho", ["Khorasani Turkish"] = "kmz", ["Khorezmian Turkic"] = "zkh", ["Khortha"] = "inc-khr", ["Khotanese"] = "kho", ["Khowar"] = "khw", ["Khroskyabs"] = "jiq", ["Khua"] = "xhv", ["Khuen"] = "khf", ["Khumi Chin"] = "cnk", ["Khvarshi"] = "khv", ["Khwarezmian"] = "xco", ["Khwe"] = "xuu", ["Kháng"] = "kjm", ["Khün"] = "kkh", ["Kiautschou German Pidgin"] = "crp-kia", ["Kibala"] = "blv", ["Kibena"] = "bez", ["Kibet"] = "kie", ["Kibiri"] = "prm", ["Kichwa"] = "qwe-kch", ["Kickapoo"] = "kic", ["Kikai"] = "kzg", ["Kikami"] = "kcu", ["Kikuyu"] = "ki", ["Kildin Sami"] = "sjd", ["Kili"] = "tuw-kli", ["Kilit"] = "xme-klt", ["Kilivila"] = "kij", ["Kiliwa"] = "klb", ["Kilmeri"] = "kih", ["Kim"] = "kia", ["Kim Mun"] = "mji", ["Kimaama"] = "kig", ["Kimaragang"] = "kqr", ["Kimbu"] = "kiv", ["Kimbundu"] = "kmb", ["Kimki"] = "sbt", ["Kimré"] = "kqp", ["Kinabalian"] = "cbw", ["Kinalakna"] = "kco", ["Kinaray-a"] = "krj", ["Kinga"] = "zga", ["Kings River Yokuts"] = "yok-kry", ["Kinikinao"] = "gqn", ["Kinnauri"] = "kfk", ["Kintaq"] = "knq", ["Kinuku"] = "kkd", ["Kioko"] = "ues", ["Kiong"] = "kkm", ["Kiorr"] = "xko", ["Kiowa"] = "kio", ["Kipchak"] = "qwm", ["Kipfokomo"] = "pkb", ["Kipsigis"] = "sgc", ["Kiput"] = "kyi", ["Kir-Balar"] = "kkr", ["Kire"] = "geb", ["Kirfi"] = "kks", ["Kirike"] = "okr", ["Kirikiri"] = "kiy", ["Kirya-Konzel"] = "fkk", ["Kis"] = "kis", ["Kisa"] = "lks", ["Kisan"] = "xis", ["Kisankasa"] = "kqh", ["Kisar"] = "kje", ["Kisi"] = "kiz", ["Kistane"] = "gru", ["Kita Maninkakan"] = "mwk", ["Kitanemuk"] = "azc-ktn", ["Kitembo"] = "tbt", ["Kitja"] = "gia", ["Kitsai"] = "kii", ["Kituba"] = "ktu", ["Kiunum"] = "wei", ["Kla"] = "lda", ["Klallam"] = "clm", ["Klamath-Modoc"] = "kla", ["Klao"] = "klu", ["Klias River Kadazan"] = "kqt", ["Klingon"] = "tlh", ["Knaanic"] = "czk", ["Ko"] = "fuj", ["Koalib"] = "kib", ["Koasati"] = "cku", ["Koba"] = "kpd", ["Kobiana"] = "kcj", ["Kobol"] = "kgu", ["Kobon"] = "kpw", ["Koch"] = "kdq", ["Kochila Tharu"] = "thq", ["Koda"] = "cdz", ["Kodaku"] = "ksz", ["Kodava"] = "kfa", ["Kodeoha"] = "vko", ["Kodi"] = "kod", ["Kodia"] = "kwp", ["Koenoem"] = "kcs", ["Kofa"] = "kso", ["Kofei"] = "kpi", ["Kofyar"] = "kwl", ["Kohin"] = "kkx", ["Kohistani Shina"] = "plk", ["Koho"] = "kpm", ["Kohumono"] = "bcs", ["Koi"] = "kkt", ["Koibal"] = "zkb", ["Koireng"] = "nkd", ["Koitabu"] = "kqi", ["Koiwat"] = "kxt", ["Kok-Nar"] = "gko", ["Kok-Paponk"] = "okg", ["Kokata"] = "ktd", ["Kokborok"] = "trp", ["Koke"] = "kou", ["Koko-Bera"] = "kkp", ["Kokoda"] = "xod", ["Kokola"] = "kzn", ["Kokota"] = "kkk", ["Kol (Cameroon)"] = "biw", ["Kol (New Guinea)"] = "kol", ["Kola"] = "kvv", ["Kolami"] = "kfb", ["Kolbila"] = "klc", ["Kolhe"] = "ekl", ["Kolibugan Subanon"] = "skn", ["Kolom"] = "klm", ["Koluwawa"] = "klx", ["Kom (Cameroon)"] = "bkm", ["Kom (India)"] = "kmm", ["Koma"] = "kmy", ["Komba"] = "kpf", ["Kombai"] = "tyn", ["Kombio"] = "xbi", ["Komering"] = "kge", ["Komi-Permyak"] = "koi", ["Komi-Yazva"] = "urj-kya", ["Komi-Zyrian"] = "kpv", ["Kominimung"] = "xoi", ["Komo"] = "xom", ["Komodo"] = "kvh", ["Kompane"] = "kvp", ["Komyandaret"] = "kzv", ["Kon Keu"] = "kkn", ["Konabéré"] = "bbo", ["Konai"] = "kxw", ["Konda"] = "knd", ["Konda-Dora"] = "kfc", ["Kondekor"] = "gau", ["Koneraw"] = "kdw", ["Kongo"] = "kg", ["Konkani"] = "kok", ["Konkomba"] = "xon", ["Konni"] = "kma", ["Kono (Guinea)"] = "knu", ["Kono (Nigeria)"] = "klk", ["Kono (Sierra Leone)"] = "kno", ["Konomala"] = "koa", ["Konomihu"] = "nai-knm", ["Konongo"] = "kcz", ["Konso"] = "kxc", ["Konyak Naga"] = "nbe", ["Konyanka Maninka"] = "mku", ["Konzo"] = "koo", ["Koonzime"] = "ozm", ["Koorete"] = "kqy", ["Kopar"] = "xop", ["Kopkaka"] = "opk", ["Korafe-Yegha"] = "kpr", ["Korak"] = "koz", ["Korana"] = "kqz", ["Korandje"] = "kcy", ["Korean"] = "ko", ["Korean Sign Language"] = "kvk", ["Koreguaje"] = "coe", ["Koresh-e Rostam"] = "okh", ["Korku"] = "kfq", ["Korlai Creole Portuguese"] = "vkp", ["Koro (India)"] = "jkr", ["Koro (Vanuatu)"] = "krf", ["Koro (West Africa)"] = "kfo", ["Koromfé"] = "kfz", ["Koromira"] = "kqj", ["Koromu"] = "xes", ["Koronadal Blaan"] = "bpr", ["Koroni"] = "xkq", ["Korop"] = "krp", ["Koropó"] = "xxr", ["Koroshi"] = "ktl", ["Korowai"] = "khe", ["Korra Koraga"] = "kfd", ["Korubo"] = "xor", ["Korupun-Sela"] = "kpq", ["Korwa"] = "kfp", ["Koryak"] = "kpy", ["Kosadle"] = "kiq", ["Kosarek Yale"] = "kkl", ["Koshin"] = "kid", ["Kosraean"] = "kos", ["Kota (Gabon)"] = "koq", ["Kota (India)"] = "kfe", ["Kota Bangun Kutai Malay"] = "mqg", ["Kota Marudu Talantang"] = "grm", ["Kotafon Gbe"] = "kqk", ["Kotava"] = "avk", ["Koti"] = "eko", ["Kott"] = "zko", ["Kou"] = "snz", ["Kouya"] = "kyf", ["Kovai"] = "kqb", ["Kove"] = "kvc", ["Kowaki"] = "xow", ["Kowiai"] = "kwh", ["Koy Sanjaq Surat"] = "kqd", ["Koya"] = "kff", ["Koyaga"] = "kga", ["Koyo"] = "koh", ["Koyra Chiini"] = "khq", ["Koyraboro Senni"] = "ses", ["Koyukon"] = "koy", ["Kpagua"] = "kuw", ["Kpala"] = "kpl", ["Kpan"] = "kpk", ["Kpasam"] = "pbn", ["Kpati"] = "koc", ["Kpatili"] = "kym", ["Kpee"] = "cpo", ["Kpelle"] = "kpe", ["Kpessi"] = "kef", ["Kplang"] = "kph", ["Krache"] = "kye", ["Krahô"] = "xra", ["Kraol"] = "rka", ["Krenak"] = "kqq", ["Kresh"] = "krs", ["Krevinian"] = "zkv", ["Kreye"] = "xre", ["Krikati-Timbira"] = "xri", ["Krim"] = "krm", ["Krio"] = "kri", ["Krisa"] = "ksi", ["Kristang"] = "mcm", ["Krobu"] = "kxb", ["Krongo"] = "kgo", ["Kru'ng"] = "krr", ["Krymchak"] = "jct", ["Kryts"] = "kry", ["Ktunaxa"] = "kut", ["Kua"] = "tyu", ["Kua-nsi"] = "ykn", ["Kuamasi"] = "yku", ["Kuan"] = "uan", ["Kuanhua"] = "xnh", ["Kube"] = "kgf", ["Kubi"] = "kof", ["Kubo"] = "jko", ["Kubu"] = "kvb", ["Kucong"] = "lkc", ["Kudiya"] = "kfg", ["Kudmali"] = "kyw", ["Kudu-Camo"] = "kov", ["Kugama"] = "kow", ["Kugbo"] = "kes", ["Kugu-Muminh"] = "xmh", ["Kui (India)"] = "kxu", ["Kui (Indonesia)"] = "kvd", ["Kuijau"] = "dkr", ["Kuikúro"] = "kui", ["Kujarge"] = "vkj", ["Kuk"] = "kfn", ["Kukatja"] = "kux", ["Kukele"] = "kez", ["Kukkuzi"] = "urj-kuk", ["Kukna"] = "kex", ["Kuku-Mangk"] = "xmq", ["Kuku-Mu'inh"] = "xmp", ["Kuku-Thaypan"] = "typ", ["Kuku-Ugbanh"] = "ugb", ["Kuku-Uwanh"] = "uwa", ["Kuku-Yalanji"] = "gvn", ["Kula"] = "tpg", ["Kulaal"] = "glj", ["Kulere"] = "kul", ["Kulfa"] = "kxj", ["Kulina"] = "xpk", ["Kulisusu"] = "vkl", ["Kullu Pahari"] = "kfx", ["Kulon"] = "uon", ["Kulung"] = "kle", ["Kumak"] = "nee", ["Kumalu"] = "ksl", ["Kumam"] = "kdi", ["Kuman"] = "kue", ["Kumaoni"] = "kfy", ["Kumarbhag Paharia"] = "kmj", ["Kumba"] = "ksm", ["Kumbainggar"] = "kgs", ["Kumbaran"] = "wkb", ["Kumbewaha"] = "xks", ["Kumeyaay"] = "nai-kum", ["Kumhali"] = "kra", ["Kumu"] = "kmw", ["Kumukio"] = "kuo", ["Kumyk"] = "kum", ["Kumzari"] = "zum", ["Kuna"] = "cuk", ["Kunama"] = "kun", ["Kunbarlang"] = "wlg", ["Kunda"] = "kdn", ["Kundal Shahi"] = "shd", ["Kunduvadi"] = "wku", ["Kung"] = "kfl", ["Kungarakany"] = "ggk", ["Kungardutyi"] = "gdt", ["Kunggari"] = "kgl", ["Kungkari"] = "lku", ["Kuni"] = "kse", ["Kuni-Boazi"] = "kvg", ["Kunigami"] = "xug", ["Kunimaipa"] = "kup", ["Kunja"] = "pep", ["Kunjen"] = "kjn", ["Kunyi"] = "njx", ["Kunza"] = "kuz", ["Kuo"] = "xuo", ["Kuot"] = "kto", ["Kupa"] = "kug", ["Kupang Malay"] = "mkn", ["Kupia"] = "key", ["Kupsabiny"] = "kpz", ["Kur"] = "kuv", ["Kura Ede Nago"] = "nqk", ["Kurama"] = "krh", ["Kuranko"] = "knk", ["Kuri"] = "nbn", ["Kuria"] = "kuj", ["Kurichiya"] = "kfh", ["Kurmukar"] = "kfv", ["Kurnai"] = "unn", ["Kurrama"] = "vku", ["Kursav"] = "faj", ["Kurti"] = "ktm", ["Kurtjar"] = "gdj", ["Kurtöp"] = "xkz", ["Kurudu"] = "kjr", ["Kurux"] = "kru", ["Kuruáya"] = "kyr", ["Kusaal"] = "kus", ["Kusaghe"] = "ksg", ["Kushi"] = "kuh", ["Kustenau"] = "awd-kus", ["Kusu"] = "ksv", ["Kusunda"] = "kgg", ["Kutang Ghale"] = "ght", ["Kutep"] = "kub", ["Kuthant"] = "xut", ["Kutto"] = "kpa", ["Kutu"] = "kdc", ["Kuturmi"] = "khj", ["Kuuk Thaayorre"] = "thd", ["Kuuk Yak"] = "uky", ["Kuuku-Ya'u"] = "kuy", ["Kuvale"] = "olu", ["Kuvi"] = "kxv", ["Kuwaa"] = "blh", ["Kuwaataay"] = "cwt", ["Kuwani"] = "paa-kwn", ["Kuy"] = "kdt", ["Kven"] = "fkv", ["Kw'adza"] = "wka", ["Kwa'"] = "bko", ["Kwaami"] = "ksq", ["Kwadi"] = "kwz", ["Kwaio"] = "kwd", ["Kwaja"] = "kdz", ["Kwak"] = "kwq", ["Kwak'wala"] = "kwk", ["Kwakum"] = "kwu", ["Kwalhioqua-Tlatskanai"] = "qwt", ["Kwama"] = "kmq", ["Kwambi"] = "kwm", ["Kwamera"] = "tnk", ["Kwami"] = "ktf", ["Kwamtim One"] = "okk", ["Kwang"] = "kvi", ["Kwanga"] = "kwj", ["Kwangali"] = "kwn", ["Kwanja"] = "knp", ["Kwanka"] = "bij", ["Kwanyama"] = "kj", ["Kwara'ae"] = "kwf", ["Kwasio"] = "nmg", ["Kwaya"] = "kya", ["Kwaza"] = "xwa", ["Kwegu"] = "xwg", ["Kwer"] = "kwr", ["Kwerba"] = "kwe", ["Kwerba Mamberamo"] = "xwr", ["Kwere"] = "cwe", ["Kwerisa"] = "kkb", ["Kwese"] = "kws", ["Kwesten"] = "kwt", ["Kwini"] = "gww", ["Kwinsu"] = "kuc", ["Kwinti"] = "kww", ["Kwoma"] = "kmo", ["Kwomtari"] = "kwo", ["Kyak"] = "bka", ["Kyaka"] = "kyc", ["Kyakala"] = "tuw-kkl", ["Kyan-Karyaw Naga"] = "nqq", ["Kyenele"] = "kql", ["Kyenga"] = "tye", ["Kyerung"] = "kgy", ["Kyrgyz"] = "ky", ["Kâte"] = "kmg", ["Kélé"] = "keb", ["Kómnzo"] = "paa-kmn", ["La'bi"] = "lbi", ["Laal"] = "gdm", ["Laalaa"] = "cae", ["Laba"] = "lau", ["Label"] = "lbb", ["Labir"] = "jku", ["Labo"] = "mwi", ["Labo Phowa"] = "ypb", ["Laboya"] = "lmy", ["Labu"] = "lbu", ["Labuk-Kinabatangan Kadazan"] = "dtb", ["Lacandon"] = "lac", ["Lachi"] = "lbt", ["Lachiguiri Zapotec"] = "zpa", ["Lachixío Zapotec"] = "zpl", ["Ladakhi"] = "lbj", ["Ladin"] = "lld", ["Ladino"] = "lad", ["Ladji-Ladji"] = "llj", ["Laeko-Libuat"] = "lkl", ["Lafofa"] = "laf", ["Laghu"] = "lgb", ["Laghuu"] = "lgh", ["Lagwan"] = "kot", ["Laha (Indonesia)"] = "lhh", ["Laha (Vietnam)"] = "lha", ["Lahanan"] = "lhn", ["Lahnda"] = "lah", ["Lahta Karen"] = "kvt", ["Lahu"] = "lhu", ["Lahu Shi"] = "lhi", ["Lahul Lohar"] = "lhl", ["Lai"] = "cnh", ["Laimbue"] = "lmx", ["Laitu Chin"] = "clj", ["Laiyolo"] = "lji", ["Lak"] = "lbe", ["Laka"] = "lak", ["Lakalei"] = "lka", ["Lake Miwok"] = "lmw", ["Lakha"] = "lkh", ["Laki"] = "lki", ["Lakkia"] = "lbc", ["Lakon"] = "lkn", ["Lakondê"] = "lkd", ["Lakota"] = "lkt", ["Lakota Dida"] = "dic", ["Lakurumau"] = "lxm", ["Lala (New Guinea)"] = "nrz", ["Lala (South Africa)"] = "bnt-lal", ["Lala-Bisa"] = "leb", ["Lala-Roba"] = "lla", ["Lalana Chinantec"] = "cnl", ["Lama Bai"] = "lay", ["Lamaholot"] = "slp", ["Lamalera"] = "lmr", ["Lamang"] = "hia", ["Lamatuka"] = "lmq", ["Lamba"] = "lam", ["Lambadi"] = "lmn", ["Lambichhong"] = "lmh", ["Lambya"] = "lai", ["Lame"] = "bma", ["Lamenu"] = "lmu", ["Lamet"] = "lbn", ["Lamja-Dengsa-Tola"] = "ldh", ["Lamkang"] = "lmk", ["Lamma"] = "lev", ["Lamnso'"] = "lns", ["Lamogai"] = "lmg", ["Lampung Api"] = "ljp", ["Lamu"] = "llh", ["Lamu-Lamu"] = "lby", ["Lanas Lobu"] = "ruu", ["Landoma"] = "ldm", ["Lang'e"] = "yne", ["Langbashe"] = "lna", ["Langi"] = "lag", ["Langnian Buyang"] = "yln", ["Lango (Sudan)"] = "lno", ["Lango (Uganda)"] = "laj", ["Lanima"] = "lnw", ["Lanoh"] = "lnh", ["Lao"] = "lo", ["Lao Naga"] = "nlq", ["Laomian"] = "lwm", ["Laopang"] = "lbg", ["Laos Sign Language"] = "lso", ["Lapaguía-Guivini Zapotec"] = "ztl", ["Lapine"] = "art-lap", ["Lapuyan Subanun"] = "laa", ["Laragia"] = "lrg", ["Larantuka Malay"] = "lrt", ["Lardil"] = "lbz", ["Larestani"] = "lrl", ["Larevat"] = "lrv", ["Larike-Wakasihu"] = "alo", ["Laro"] = "lro", ["Larteh"] = "lar", ["Laru"] = "lan", ["Lasalimu"] = "llm", ["Lasgerdi"] = "lsa", ["Lashi"] = "lsi", ["Lasi"] = "lss", ["Latgalian"] = "ltg", ["Latin"] = "la", ["Latu"] = "ltu", ["Latundê"] = "ltn", ["Latvian"] = "lv", ["Latvian Sign Language"] = "lsl", ["Lau"] = "llu", ["Laua"] = "luf", ["Lauan"] = "llx", ["Lauje"] = "law", ["Laura"] = "lur", ["Laurentian"] = "lre", ["Lavatbura-Lamusong"] = "lbv", ["Lave"] = "brb", ["Laven"] = "lbo", ["Lavukaleve"] = "lvk", ["Lawangan"] = "lbx", ["Lawi"] = "lvi", ["Lawu"] = "lwu", ["Lawunuia"] = "tgi", ["Layakha"] = "lya", ["Laz"] = "lzz", ["Laze"] = "tbq-laz", ["Lealao Chinantec"] = "cle", ["Leco"] = "lec", ["Ledo Kaili"] = "lew", ["Leelau"] = "ldk", ["Lefa"] = "lfa", ["Lega-Mwenga"] = "lgm", ["Lega-Shabunda"] = "lea", ["Legbo"] = "agb", ["Legenyem"] = "lcc", ["Lehali"] = "tql", ["Leinong Naga"] = "lzn", ["Leipon"] = "lek", ["Leitre"] = "paa-lei", ["Leizhou Min"] = "luh", ["Lela"] = "dri", ["Lelak"] = "llk", ["Lele (Chad)"] = "lln", ["Lele (Congo)"] = "lel", ["Lele (Guinea)"] = "llc", ["Lele (New Guinea)"] = "lle", ["Lelemi"] = "lef", ["Lelepa"] = "lpa", ["Lembena"] = "leq", ["Lemerig"] = "lrz", ["Lemio"] = "lei", ["Lemnian"] = "xle", ["Lemolang"] = "ley", ["Lemoro"] = "ldj", ["Lenakel"] = "tnl", ["Lendu"] = "led", ["Lengilu"] = "lgi", ["Lengo"] = "lgr", ["Lengola"] = "lej", ["Lenje"] = "leh", ["Lenkau"] = "ler", ["Lenyima"] = "ldg", ["Leonese"] = "roa-leo", ["Lepcha"] = "lep", ["Lepki"] = "lpe", ["Lepontic"] = "xlp", ["Lere"] = "gnh", ["Lese"] = "les", ["Lesing-Gelimi"] = "let", ["Letemboi"] = "nms", ["Leti"] = "lti", ["Levuka"] = "lvu", ["Lewo"] = "lww", ["Lewo Eleng"] = "lwe", ["Lewotobi"] = "lwt", ["Leyigha"] = "ayi", ["Lezgi"] = "lez", ["Lhao Vo"] = "mhx", ["Lhokpu"] = "lhp", ["Li'o"] = "ljl", ["Liabuku"] = "lix", ["Liana-Seti"] = "ste", ["Liangmai Naga"] = "njn", ["Liberia Kpelle"] = "xpe", ["Liberian Kreyol"] = "lir", ["Libido"] = "liq", ["Libinza"] = "liz", ["Libon Bikol"] = "lbl", ["Liburnian"] = "xli", ["Libyan Arabic"] = "ayl", ["Libyan Sign Language"] = "lbs", ["Ligbi"] = "lig", ["Ligenza"] = "lgz", ["Ligurian"] = "lij", ["Lihir"] = "lih", ["Lika"] = "lik", ["Liki"] = "lio", ["Likila"] = "lie", ["Likuba"] = "kxx", ["Likum"] = "lib", ["Likwala"] = "kwc", ["Lilau"] = "lll", ["Lillooet"] = "lil", ["Limassa"] = "bme", ["Limbu"] = "lif", ["Limbum"] = "lmp", ["Limburgish"] = "li", ["Limi"] = "ylm", ["Limilngan"] = "lmc", ["Limos Kalinga"] = "kmk", ["Lindu"] = "klw", ["Linear A"] = "lab", ["Lingala"] = "ln", ["Lingao"] = "onb", ["Lingkhim"] = "lii", ["Lingua Franca Nova"] = "lfn", ["Linngithigh"] = "lnj", ["Lipan"] = "apl", ["Lipo"] = "lpo", ["Lisabata-Nuniali"] = "lcs", ["Lisela"] = "lcl", ["Lish"] = "lsh", ["Lishana Deni"] = "lsd", ["Lishanid Noshan"] = "aij", ["Lishán Didán"] = "trg", ["Lisu"] = "lis", ["Lithuanian"] = "lt", ["Lithuanian Sign Language"] = "lls", ["Little Swanport"] = "aus-lsw", ["Litzlitz"] = "lzl", ["Livonian"] = "liv", ["Livvi"] = "olo", ["Lizu"] = "sit-liz", ["Lo-Toga"] = "lht", ["Loarki"] = "lrk", ["Lobala"] = "loq", ["Lobi"] = "lob", ["Lodhi"] = "lbm", ["Logba"] = "lgq", ["Logo"] = "log", ["Logol"] = "lof", ["Logooli"] = "rag", ["Logorik"] = "liu", ["Lojban"] = "jbo", ["Lokaa"] = "yaz", ["Loko"] = "lok", ["Lokono"] = "arw", ["Lokoya"] = "lky", ["Lola"] = "lcd", ["Lolak"] = "llq", ["Lole"] = "llg", ["Lolo"] = "llb", ["Loloda"] = "loa", ["Lolopo"] = "ycl", ["Loma"] = "lom", ["Lomaiviti"] = "lmv", ["Lomakka"] = "loi", ["Lomavren"] = "rmi", ["Lombard"] = "lmo", ["Lombi"] = "lmi", ["Lombo"] = "loo", ["Lomwe"] = "ngl", ["Loncong"] = "lce", ["Long Phuri Naga"] = "lpn", ["Long Wat"] = "ttw", ["Longgu"] = "lgu", ["Longjia"] = "sit-lnj", ["Longto"] = "wok", ["Longuda"] = "lnu", ["Longyan Min"] = "nan-lnx", ["Loniu"] = "los", ["Lonwolwol"] = "crc", ["Loo"] = "ldo", ["Lopa"] = "lop", ["Lope"] = "yiu", ["Lopi"] = "lov", ["Lopit"] = "lpx", ["Lorang"] = "lrn", ["Lorediakarkar"] = "lnn", ["Lorrain"] = "roa-lor", ["Lote"] = "uvl", ["Lotha Naga"] = "njh", ["Lotud"] = "dtr", ["Lotuko"] = "lot", ["Lou"] = "loj", ["Louisiana Creole"] = "lou", ["Loun"] = "lox", ["Loup A"] = "xlo", ["Loup B"] = "xlb", ["Lovono"] = "vnk", ["Low German"] = "nds", ["Lower Burdekin"] = "xbb", ["Lower Chehalis"] = "cea", ["Lower Grand Valley Dani"] = "dni", ["Lower Mandobo"] = "bwp", ["Lower Nossob"] = "nsb", ["Lower Sorbian"] = "dsb", ["Lower Southern Aranda"] = "axl", ["Lower Ta'oih"] = "tto", ["Lower Tanana"] = "taa", ["Lowland Oaxaca Chontal"] = "clo", ["Lowland Tarahumara"] = "tac", ["Loxicha Zapotec"] = "ztp", ["Lozi"] = "loz", ["Luang"] = "lex", ["Luba-Kasai"] = "lua", ["Luba-Katanga"] = "lu", ["Lubila"] = "kcc", ["Lubu"] = "lcf", ["Lubuagan Kalinga"] = "knb", ["Luchazi"] = "lch", ["Lucumí"] = "luq", ["Ludian"] = "lud", ["Lufu"] = "ldq", ["Luganda"] = "lg", ["Lugbara"] = "lgg", ["Luguru"] = "ruf", ["Luhu"] = "lcq", ["Luhya"] = "luy", ["Luimbi"] = "lum", ["Luiseño"] = "lui", ["Lukpa"] = "dop", ["Lule"] = "ule", ["Lule Sami"] = "smj", ["Lumba-Yakkha"] = "luu", ["Lumbu"] = "lup", ["Lumun"] = "lmd", ["Lun Bawang"] = "lnd", ["Luna"] = "luj", ["Lunanakha"] = "luk", ["Lunda"] = "lun", ["Lungga"] = "lga", ["Luo"] = "luo", ["Luopohe Hmong"] = "hml", ["Luren"] = "sit-lrn", ["Luri (Nigeria)"] = "ldd", ["Lusengo"] = "lse", ["Lushootseed"] = "lut", ["Lusi"] = "khl", ["Lusitanian"] = "xls", ["Lutachoni"] = "lts", ["Lutos"] = "ndy", ["Lutuv"] = "clt", ["Luvale"] = "lue", ["Luwati"] = "luv", ["Luwian"] = "xlu", ["Luwo"] = "lwo", ["Luxembourgish"] = "lb", ["Luyana"] = "lyn", ["Lwalu"] = "lwa", ["Lwel"] = "lvl", ["Lycian"] = "xlc", ["Lydian"] = "xld", ["Lyngngam"] = "lyg", ["Lyélé"] = "lee", ["Láadan"] = "ldn", ["Láá Láá Bwamu"] = "bwj", ["Löyöp"] = "urr", ["Lü"] = "khb", ["Ma"] = "msj", ["Ma Manda"] = "skc", ["Ma'anyan"] = "mhy", ["Ma'di"] = "mhi", ["Ma'ya"] = "slz", ["Maaka"] = "mew", ["Maale"] = "mdy", ["Maasai"] = "mas", ["Maay"] = "ymm", ["Maba"] = "mqa", ["Mabaale"] = "mmz", ["Mabaan"] = "mfz", ["Mabaka Valley Kalinga"] = "kkg", ["Mabire"] = "muj", ["Maca"] = "mca", ["Macaguaje"] = "mcl", ["Macaguán"] = "mbn", ["Macanese"] = "mzs", ["Macau Pidgin Portuguese"] = "crp-mpp", ["Macedonian"] = "mk", ["Machame"] = "jmc", ["Machiguenga"] = "mcb", ["Machinere"] = "mpd", ["Machinga"] = "mvw", ["Macoris"] = "nai-mac", ["Macuna"] = "myy", ["Macushi"] = "mbc", ["Mada (Cameroon)"] = "mxu", ["Mada (Nigeria)"] = "mda", ["Madagascar Sign Language"] = "mzc", ["Madak"] = "mmx", ["Maden"] = "xmx", ["Madhi Madhi"] = "dmd", ["Madi"] = "grg", ["Madngele"] = "zml", ["Madukayang Kalinga"] = "kmd", ["Madurese"] = "mad", ["Maeng Itneg"] = "itt", ["Mafa"] = "maf", ["Mag-Anchi Ayta"] = "sgb", ["Mag-Indi Ayta"] = "blx", ["Magahat"] = "mtw", ["Magahi"] = "mag", ["Magdalena Peñasco Mixtec"] = "xtm", ["Magi"] = "gkd", ["Magiyi"] = "gmg", ["Magoma"] = "gmx", ["Magori"] = "zgr", ["Maguindanao"] = "mdh", ["Mahali"] = "mjx", ["Mahasu Pahari"] = "bfz", ["Mahican"] = "mjy", ["Mahongwe"] = "mhb", ["Mahou"] = "mxx", ["Maia"] = "sks", ["Maiadomu"] = "mzz", ["Maiani"] = "tnh", ["Maii"] = "mmm", ["Mailu"] = "mgu", ["Maindo"] = "cwb", ["Maipure"] = "awd-mpr", ["Mairasi"] = "zrs", ["Maisin"] = "mbq", ["Maithili"] = "mai", ["Maiwa (Indonesia)"] = "wmm", ["Maiwa (New Guinea)"] = "mti", ["Maiwala"] = "mum", ["Majang"] = "mpe", ["Majera"] = "xmj", ["Majhi"] = "mjz", ["Majhwar"] = "mmj", ["Mak (China)"] = "mkg", ["Mak (Nigeria)"] = "pbl", ["Makaa"] = "mcp", ["Makah"] = "myh", ["Makalero"] = "mjb", ["Makasae"] = "mkz", ["Makasar"] = "mak", ["Makassar Malay"] = "mfp", ["Makayam"] = "aup", ["Makhuwa"] = "vmw", ["Makhuwa-Marrevone"] = "xmc", ["Makhuwa-Meetto"] = "mgh", ["Makhuwa-Moniga"] = "mhm", ["Makhuwa-Saka"] = "xsq", ["Makhuwa-Shirima"] = "vmk", ["Maklew"] = "mgf", ["Makolkol"] = "zmh", ["Makonde"] = "kde", ["Maku"] = "xak", ["Maku'a"] = "lva", ["Makuri Naga"] = "jmn", ["Makuráp"] = "mpu", ["Makwe"] = "ymk", ["Makyan Naga"] = "umn", ["Mal"] = "mlf", ["Mal Paharia"] = "mkb", ["Mala (New Guinea)"] = "ped", ["Mala (Nigeria)"] = "ruy", ["Mala Malasar"] = "ima", ["Malaccan Creole Malay"] = "ccm", ["Malagasy"] = "mg", ["Malalamai"] = "mmt", ["Malalí"] = "sai-mal", ["Malango"] = "mln", ["Malankuravan"] = "mjo", ["Malapandaram"] = "mjp", ["Malaryan"] = "mjq", ["Malas"] = "mkr", ["Malasanga"] = "mqz", ["Malasar"] = "ymr", ["Malavedan"] = "mjr", ["Malawi Lomwe"] = "lon", ["Malawian Sign Language"] = "lws", ["Malay"] = "ms", ["Malayalam"] = "ml", ["Malayic Dayak"] = "xdy", ["Malaynon"] = "mlz", ["Malaysian Sign Language"] = "xml", ["Malba Birifor"] = "bfo", ["Male"] = "mdc", ["Malecite-Passamaquoddy"] = "pqm", ["Maleng"] = "pkt", ["Maleu-Kilenge"] = "mgl", ["Malfaxal"] = "mlx", ["Malgana"] = "vml", ["Malgbe"] = "mxf", ["Mali"] = "gcc", ["Malibu"] = "sai-mlb", ["Malila"] = "mgq", ["Malimba"] = "mzd", ["Malimpung"] = "mli", ["Malinaltepec Tlapanec"] = "tcf", ["Malol"] = "mbk", ["Maltese"] = "mt", ["Maltese Sign Language"] = "mdl", ["Malua Bay"] = "mll", ["Malvi"] = "mup", ["Maléku Jaíka"] = "gut", ["Mam"] = "mam", ["Mama"] = "mma", ["Mamaa"] = "mhf", ["Mamaindé"] = "wmd", ["Mamanwa"] = "mmn", ["Mamara"] = "myk", ["Mamasa"] = "mqj", ["Mambae"] = "mgm", ["Mambai"] = "mcs", ["Mamboru"] = "mvd", ["Mambwe-Lungu"] = "mgr", ["Mampruli"] = "maw", ["Mamuju"] = "mqx", ["Mamulique"] = "emm", ["Mamusi"] = "kdf", ["Mamvu"] = "mdi", ["Man Met"] = "mml", ["Manado Malay"] = "xmm", ["Manam"] = "mva", ["Manambu"] = "mle", ["Manangba"] = "nmm", ["Manangkari"] = "znk", ["Manao"] = "awd-man", ["Manat"] = "pmr", ["Manchu"] = "mnc", ["Manda (Australia)"] = "zma", ["Manda (India)"] = "mha", ["Manda (Tanzania)"] = "mgs", ["Mandahuaca"] = "mht", ["Mandaic"] = "mid", ["Mandailing Batak"] = "btm", ["Mandalorian"] = "art-man", ["Mandan"] = "mhq", ["Mandandanyi"] = "zmk", ["Mandar"] = "mdr", ["Mandara"] = "tbf", ["Mandari"] = "mqu", ["Mandarin"] = "cmn", ["Mandeali"] = "mjl", ["Mander"] = "mqr", ["Mandingo"] = "man", ["Mandinka"] = "mnk", ["Mandjak"] = "mfv", ["Manem"] = "jet", ["Mang"] = "zng", ["Mangala"] = "mem", ["Mangarayi"] = "mpc", ["Mangarevan"] = "mrv", ["Mangas"] = "zns", ["Mangayat"] = "myj", ["Mangbetu"] = "mdj", ["Mangbutu"] = "mdk", ["Mangerr"] = "zme", ["Mangga Buang"] = "mmo", ["Manggarai"] = "mqy", ["Mangghuer"] = "mjg-mgr", ["Mango"] = "mge", ["Mangole"] = "mqc", ["Mangseng"] = "mbh", ["Mangue"] = "mom", ["Manigri-Kambolé Ede Nago"] = "xkb", ["Manipa"] = "mqp", ["Manipuri"] = "mni", ["Mankanya"] = "knf", ["Mankiyali"] = "nlm", ["Manna-Dora"] = "mju", ["Mannan"] = "mjv", ["Mano"] = "mev", ["Manombai"] = "woo", ["Mansaka"] = "msk", ["Mansoanka"] = "msw", ["Manta"] = "myg", ["Mantsi"] = "nty", ["Manumanaw Karen"] = "kxf", ["Manus Koro"] = "kxr", ["Manusela"] = "wha", ["Manx"] = "gv", ["Manya"] = "mzj", ["Manyawa"] = "mny", ["Manza"] = "mzv", ["Mao Naga"] = "nbi", ["Maonan"] = "mmd", ["Maore Comorian"] = "swb", ["Mape"] = "mlh", ["Mapena"] = "mnm", ["Mapia"] = "mpy", ["Mapidian"] = "mpw", ["Mapos Buang"] = "bzh", ["Mapoyo"] = "mcg", ["Mapudungun"] = "arn", ["Mapun"] = "sjm", ["Mara"] = "mec", ["Mara Chin"] = "mrh", ["Marachi"] = "lri", ["Maraghei"] = "vmh", ["Maragus"] = "mrs", ["Maram Naga"] = "nma", ["Marama"] = "lrm", ["Maranao"] = "mrw", ["Maranungku"] = "zmr", ["Mararit"] = "mgb", ["Marathi"] = "mr", ["Maratino"] = "sai-mar", ["Marau"] = "mvr", ["Marawan"] = "awd-mar", ["Marba"] = "mpg", ["Marenje"] = "vmr", ["Marfa"] = "mvu", ["Margany"] = "zmc", ["Marghi South"] = "mfm", ["Margi"] = "mrt", ["Mari (Austronesian)"] = "hob", ["Mari (Sepik)"] = "mbx", ["Maria"] = "mds", ["Mariaté"] = "awd-mrt", ["Maricopa"] = "mrc", ["Maridan"] = "zmd", ["Maridjabin"] = "zmj", ["Marik"] = "dad", ["Marimanindji"] = "zmm", ["Marind"] = "mrz", ["Maring"] = "mbw", ["Maring Naga"] = "nng", ["Maringarr"] = "zmt", ["Mariri"] = "mqi", ["Maritime Sign Language"] = "nsr", ["Maritsauá"] = "msp", ["Mariupol Greek"] = "grk-mar", ["Mariyedi"] = "zmy", ["Marka"] = "rkm", ["Markweeta"] = "enb", ["Marma"] = "rmz", ["Maroon Spirit Language"] = "crp-mar", ["Marovo"] = "mvo", ["Marrgu"] = "mhg", ["Marriammu"] = "xru", ["Marrithiyel"] = "mfr", ["Marrucinian"] = "umc", ["Marshallese"] = "mh", ["Marsian"] = "ims", ["Martha's Vineyard Sign Language"] = "mre", ["Marti Ke"] = "zmg", ["Martu Wangka"] = "mpj", ["Martuthunira"] = "vma", ["Marwari"] = "mwr", ["Marúbo"] = "mzr", ["Masaba"] = "myx", ["Masadiit Itneg"] = "tis", ["Masakará"] = "sai-msk", ["Masalit"] = "mls", ["Masana"] = "mcn", ["Masbate Sorsogon"] = "bks", ["Masbatenyo"] = "msb", ["Mashco Piro"] = "cuj", ["Mashi"] = "mho", ["Masimasi"] = "ism", ["Masiwang"] = "bnf", ["Maskelynes"] = "klv", ["Maslam"] = "msv", ["Masmaje"] = "mes", ["Massachusett"] = "wam", ["Massalat"] = "mdg", ["Massep"] = "mvs", ["Matagalpa"] = "mtn", ["Matal"] = "mfh", ["Matanawi"] = "sai-mat", ["Matbat"] = "xmt", ["Matengo"] = "mgv", ["Matepi"] = "mqe", ["Matigsalug Manobo"] = "mbt", ["Matipuhy"] = "mzo", ["Matlatzinca"] = "mat", ["Mato"] = "met", ["Mator"] = "mtm", ["Matsés"] = "mcf", ["Mattole"] = "mvb", ["Matukar"] = "mjk", ["Matumbi"] = "mgw", ["Matya Samo"] = "stj", ["Matís"] = "mpq", ["Maung"] = "mph", ["Mauritian Creole"] = "mfe", ["Mauritian Sign Language"] = "lsy", ["Mauwake"] = "mhl", ["Mav̋ea"] = "mkv", ["Mawa"] = "mcw", ["Mawak"] = "mjj", ["Mawan"] = "mcz", ["Mawayana"] = "mzx", ["Mawchi"] = "mke", ["Mawes"] = "mgk", ["Maxakalí"] = "mbl", ["Maxi Gbe"] = "mxl", ["Maya Samo"] = "sym", ["Mayaguduna"] = "xmy", ["Mayangna"] = "yan", ["Mayawali"] = "yxa", ["Maybrat"] = "ayz", ["Mayeka"] = "myc", ["Mayi-Thakurti"] = "xyt", ["Maykulan"] = "mnt", ["Maynas"] = "sai-mys", ["Mayo"] = "mfy", ["Mayogo"] = "mdm", ["Mayoyao Ifugao"] = "ifu", ["Mazagway"] = "dkx", ["Mazaltepec Zapotec"] = "zpy", ["Mazanderani"] = "mzn", ["Mazatlán Mazatec"] = "vmz", ["Mazatlán Mixe"] = "mzl", ["Mba"] = "mfc", ["Mbabaram"] = "vmb", ["Mbala"] = "mdp", ["Mbalanhu"] = "lnb", ["Mbandja"] = "zmz", ["Mbangala"] = "mxg", ["Mbangi"] = "mgn", ["Mbangwe"] = "zmn", ["Mbara (Australia)"] = "mvl", ["Mbara (Chad)"] = "mpk", ["Mbariman-Gudhinma"] = "zmv", ["Mbati"] = "mdn", ["Mbato"] = "gwa", ["Mbay"] = "myb", ["Mbe"] = "mfo", ["Mbe'"] = "mtk", ["Mbelime"] = "mql", ["Mbere"] = "mdt", ["Mbesa"] = "zms", ["Mbiywom"] = "aus-mbi", ["Mbo (Cameroon)"] = "mbo", ["Mbo (Congo)"] = "zmw", ["Mboi"] = "moi", ["Mboko"] = "mdu", ["Mbole"] = "mdq", ["Mbonga"] = "xmb", ["Mbongno"] = "bgu", ["Mbosi"] = "mdw", ["Mbowe"] = "mxo", ["Mbre"] = "mka", ["Mbu'"] = "muc", ["Mbudum"] = "xmd", ["Mbugu"] = "mhd", ["Mbugwe"] = "mgz", ["Mbuko"] = "mqb", ["Mbukushu"] = "mhw", ["Mbula"] = "mna", ["Mbula-Bwazza"] = "mbu", ["Mbule"] = "mlb", ["Mbulungish"] = "mbv", ["Mbum"] = "mdd", ["Mbunda"] = "mck", ["Mbunga"] = "mgy", ["Mburku"] = "bbt", ["Mbuun"] = "zmp", ["Mbwela"] = "mfu", ["Mbya Guarani"] = "gun", ["Me'en"] = "mym", ["Mebu"] = "mjn", ["Mecayapan Nahuatl"] = "nhx", ["Medebur"] = "mjm", ["Medefaidrin"] = "dmf", ["Media Lengua"] = "mue", ["Mednyj Aleut"] = "mud", ["Medumba"] = "byv", ["Mefele"] = "mfj", ["Megam"] = "mef", ["Megleno-Romanian"] = "ruq", ["Mehek"] = "nux", ["Mehináku"] = "mmh", ["Mehri"] = "gdq", ["Mekeo"] = "mek", ["Mekmek"] = "mvk", ["Mekwei"] = "msf", ["Mekéns"] = "skf", ["Mel-Khaonh"] = "hkn", ["Mele-Fila"] = "mxe", ["Melo"] = "mfx", ["Melpa"] = "med", ["Memoni"] = "mby", ["Mendalam Kayan"] = "xkd", ["Mendankwe-Nkwen"] = "mfd", ["Mende (New Guinea)"] = "sim", ["Mende (Sierra Leone)"] = "men", ["Mengaka"] = "xmg", ["Mengen"] = "mee", ["Mengisa"] = "leo", ["Menien"] = "sai-men", ["Menka"] = "mea", ["Menominee"] = "mez", ["Mentawai"] = "mwv", ["Menya"] = "mcr", ["Meoswar"] = "mvx", ["Mer"] = "mnu", ["Meramera"] = "mxm", ["Merei"] = "lmb", ["Merey"] = "meq", ["Meriam"] = "ulk", ["Meroitic"] = "xmr", ["Meru"] = "mer", ["Mesaka"] = "iyo", ["Mese"] = "mci", ["Mesme"] = "zim", ["Mesmes"] = "mys", ["Mesqan"] = "mvz", ["Messapic"] = "cms", ["Meta'"] = "mgo", ["Metlatónoc Mixtec"] = "mxv", ["Mewari"] = "mtr", ["Mewati"] = "wtm", ["Mexican Sign Language"] = "mfs", ["Meyah"] = "mej", ["Mezontla Popoloca"] = "pbe", ["Mezquital Otomi"] = "ote", ["Meänkieli"] = "fit", ["Mfinu"] = "zmf", ["Mfumte"] = "nfu", ["Mgbo"] = "gmz", ["Mi'kmaq"] = "mic", ["Miami"] = "mia", ["Mian"] = "mpt", ["Miani"] = "pla", ["Micha"] = "yiq", ["Michif"] = "crg", ["Michigamea"] = "cmm", ["Michoacán Mazahua"] = "mmc", ["Michoacán Nahuatl"] = "ncl", ["Mid Grand Valley Dani"] = "dnt", ["Mid-Southern Banda"] = "bjo", ["Middle Armenian"] = "axm", ["Middle Assamese"] = "inc-mas", ["Middle Bengali"] = "inc-mbn", ["Middle Breton"] = "xbm", ["Middle Chinese"] = "ltc", ["Middle Cornish"] = "cnx", ["Middle Dutch"] = "dum", ["Middle English"] = "enm", ["Middle French"] = "frm", ["Middle Gujarati"] = "inc-mgu", ["Middle High German"] = "gmh", ["Middle Irish"] = "mga", ["Middle Kannada"] = "dra-mkn", ["Middle Khmer"] = "xhm", ["Middle Korean"] = "okm", ["Middle Low German"] = "gml", ["Middle Median"] = "xme-mid", ["Middle Mon"] = "mkh-mmn", ["Middle Mongol"] = "xng", ["Middle Newar"] = "nwx", ["Middle Norwegian"] = "gmq-mno", ["Middle Odia"] = "inc-mor", ["Middle Persian"] = "pal", ["Middle Scots"] = "gmw-msc", ["Middle Vietnamese"] = "mkh-mvi", ["Middle Watut"] = "mpl", ["Middle Welsh"] = "wlm", ["Midob"] = "mei", ["Migaama"] = "mmy", ["Migabac"] = "mpp", ["Miji"] = "sjl", ["Miju"] = "mxj", ["Mikasuki"] = "mik", ["Milang"] = "tbq-mil", ["Mili"] = "ymh", ["Millcayac"] = "sai-mil", ["Miltu"] = "mlj", ["Miluk"] = "iml", ["Milyan"] = "imy", ["Mimi of Decorse"] = "mis-mmd", ["Mimi of Nachtigal"] = "mis-mmn", ["Min Nan"] = "nan", ["Mina"] = "hna", ["Minaean"] = "inm", ["Minang"] = "xrg", ["Minangkabau"] = "min", ["Minaveha"] = "mvn", ["Minderico"] = "drc", ["Mindiri"] = "mpn", ["Mingang Doso"] = "mko", ["Mingo"] = "iro-min", ["Mingrelian"] = "xmf", ["Minica Huitoto"] = "hto", ["Minidien"] = "wii", ["Minjungbal"] = "xjb", ["Minkin"] = "xxm", ["Minoan"] = "omn", ["Minokok"] = "mqq", ["Minriq"] = "mnq", ["Mintil"] = "mzt", ["Mirandese"] = "mwl", ["Mire"] = "mvh", ["Mirgan"] = "zrg", ["Miriti"] = "mmv", ["Miriwoong"] = "mep", ["Miriwoong Sign Language"] = "rsm", ["Mirning"] = "gmr", ["Misantla Totonac"] = "tlc", ["Miship"] = "mjs", ["Misima-Paneati"] = "mpx", ["Mising"] = "mrg", ["Miskito"] = "miq", ["Mitla Zapotec"] = "zaw", ["Mitlatongo Mixtec"] = "vmm", ["Mittu"] = "mwu", ["Mituku"] = "zmq", ["Miu"] = "mpo", ["Miwa"] = "vmi", ["Mixed Great Andamanese"] = "gac", ["Mixifore"] = "mfg", ["Mixtepec Mixtec"] = "mix", ["Mixtepec Zapotec"] = "zpm", ["Miya"] = "mkf", ["Miyako"] = "mvi", ["Miyobe"] = "soy", ["Mizo"] = "lus", ["Mlabri"] = "mra", ["Mlahsö"] = "lhs", ["Mlap"] = "kja", ["Mlomp"] = "mlo", ["Mmaala"] = "mmu", ["Mmani"] = "buy", ["Mmen"] = "bfm", ["Mo"] = "wkd", ["Mo'da"] = "gbn", ["Moabite"] = "obm", ["Moba"] = "mfq", ["Mobilian"] = "mod", ["Mobu"] = "ahm", ["Mocana"] = "sai-mcn", ["Mochi"] = "old", ["Mochica"] = "omc", ["Mocho"] = "mhc", ["Mocoví"] = "moc", ["Modang"] = "mxd", ["Modole"] = "mqo", ["Moere"] = "mvq", ["Mofu-Gudur"] = "mif", ["Mogholi"] = "mhj", ["Mogum"] = "mou", ["Mohawk"] = "moh", ["Mohegan-Pequot"] = "xpq", ["Moi (Congo)"] = "mow", ["Moi (Indonesia)"] = "mxn", ["Moikodi"] = "mkp", ["Moingi"] = "mwz", ["Mojave"] = "mov", ["Moji"] = "ymi", ["Mok"] = "mqt", ["Mokati"] = "wnb", ["Moken"] = "mwt", ["Mokerang"] = "mft", ["Mokilese"] = "mkj", ["Moklen"] = "mkm", ["Mokole"] = "mkl", ["Mokpwe"] = "bri", ["Moksha"] = "mdf", ["Molale"] = "mbe", ["Molbog"] = "pwm", ["Moldova Sign Language"] = "vsi", ["Molengue"] = "bxc", ["Molima"] = "mox", ["Molmo One"] = "aun", ["Molo"] = "zmo", ["Molof"] = "msl", ["Moloko"] = "mlw", ["Mom Jango"] = "ver", ["Moma"] = "myl", ["Momare"] = "msz", ["Mombo Dogon"] = "dmb", ["Mombum"] = "mso", ["Momina"] = "mmb", ["Momuna"] = "mqf", ["Mon"] = "mnw", ["Monastic Sign Language"] = "mzg", ["Mondropolon"] = "npn", ["Mondé"] = "mnd", ["Mongghul"] = "mjg-mgl", ["Mongo"] = "lol", ["Mongolian"] = "mn", ["Mongolian Sign Language"] = "msr", ["Mongondow"] = "mog", ["Mongsen Ao"] = "njo-mng", ["Moni"] = "mnz", ["Mono (California)"] = "mnr", ["Mono (Cameroon)"] = "mru", ["Mono (Congo)"] = "mnh", ["Monom"] = "moo", ["Monsang Naga"] = "nmh", ["Montagnais"] = "moe", ["Montana Salish"] = "fla", ["Montol"] = "mtl", ["Monumbo"] = "mxk", ["Monzombo"] = "moj", ["Moo"] = "gwg", ["Moore"] = "mos", ["Moose Cree"] = "crm", ["Mopan Maya"] = "mop", ["Mor (Austronesian)"] = "mhz", ["Mor (Papuan)"] = "moq", ["Moraid"] = "msg", ["Moran"] = "tbq-mor", ["Morawa"] = "mze", ["Morelos Nahuatl"] = "nhm", ["Morerebi"] = "xmo", ["Moresada"] = "msx", ["Mori Atas"] = "mzq", ["Mori Bawah"] = "xmz", ["Morigi"] = "mdb", ["Moriori"] = "rrm", ["Moro"] = "mor", ["Moroccan Amazigh"] = "zgh", ["Moroccan Arabic"] = "ary", ["Moroccan Sign Language"] = "xms", ["Morokodo"] = "mgc", ["Morom"] = "bdo", ["Moronene"] = "mqn", ["Morori"] = "mok", ["Morouas"] = "mrp", ["Mortlockese"] = "mrl", ["Moru"] = "mgd", ["Mosimo"] = "mqv", ["Moskona"] = "mtj", ["Mota"] = "mtt", ["Motembo"] = "tmv", ["Motu"] = "meu", ["Mouk-Aria"] = "mwh", ["Mount Iraya Agta"] = "atl", ["Mount Iriga Agta"] = "agz", ["Mountain Koiari"] = "kpx", ["Mouwase"] = "jmw", ["Movima"] = "mzp", ["Moyadan Itneg"] = "ity", ["Moyon Naga"] = "nmo", ["Mozambican Sign Language"] = "mzy", ["Mozarabic"] = "mxi", ["Mpade"] = "mpi", ["Mpalitjanh"] = "xpj", ["Mpi"] = "mpz", ["Mpiemo"] = "mcx", ["Mpiin"] = "bnt-mpi", ["Mpinda"] = "pnd", ["Mpongmpong"] = "mgg", ["Mpoto"] = "mpa", ["Mpotovoro"] = "mvt", ["Mpuono"] = "bnt-mpu", ["Mpur"] = "akc", ["Mro Chin"] = "cmr", ["Mru"] = "mro", ["Mser"] = "kqx", ["Muak Sa-aak"] = "ukk", ["Mualang"] = "mtd", ["Mubami"] = "tsx", ["Mubi"] = "mub", ["Mucuchí"] = "sai-muc", ["Muda"] = "ymd", ["Mudburra"] = "dmw", ["Mudu Koraga"] = "vmd", ["Muduapa"] = "wiv", ["Muduga"] = "udg", ["Muellama"] = "sai-mue", ["Mufian"] = "aoj", ["Muher"] = "sem-mhr", ["Muinane"] = "bmr", ["Mukha-Dora"] = "mmk", ["Mukulu"] = "moz", ["Mulaha"] = "mfw", ["Mulam"] = "mlm", ["Mulao"] = "giu", ["Mullu Kurumba"] = "kpb", ["Mullukmulluk"] = "mpb", ["Muluridyi"] = "vmu", ["Mum"] = "kqa", ["Mumuye"] = "mzm", ["Muna"] = "mnb", ["Munda"] = "unx", ["Mundabli"] = "boe", ["Mundang"] = "mua", ["Mundani"] = "mnf", ["Mundari"] = "unr", ["Mundat"] = "mmf", ["Mundolinco"] = "art-mun", ["Mundurukú"] = "myu", ["Mungaka"] = "mhk", ["Mungbam"] = "mij", ["Munggui"] = "mth", ["Mungkip"] = "mpv", ["Muniche"] = "myr", ["Munit"] = "mtc", ["Munji"] = "mnj", ["Munsee"] = "umu", ["Muong"] = "mtq", ["Mur Pano"] = "tkv", ["Muratayak"] = "asx", ["Murik (Malaysia)"] = "mxr", ["Murik (New Guinea)"] = "mtf", ["Murkim"] = "rmh", ["Murle"] = "mur", ["Murrinh-Patha"] = "mwf", ["Mursi"] = "muz", ["Murui Huitoto"] = "huu", ["Murupi"] = "mqw", ["Muruwari"] = "zmu", ["Musan"] = "mmp", ["Musasa"] = "smm", ["Musey"] = "mse", ["Musgu"] = "mug", ["Musi"] = "mui", ["Muskum"] = "mje", ["Musom"] = "msu", ["Mussau-Emira"] = "emi", ["Muthuvan"] = "muv", ["Mutu"] = "tuc", ["Muya"] = "mvm", ["Muyang"] = "muy", ["Muyuw"] = "myw", ["Muzi"] = "ymz", ["Muzo"] = "sai-muz", ["Mvanip"] = "mcj", ["Mvuba"] = "mxh", ["Mwaghavul"] = "sur", ["Mwakai"] = "mgt", ["Mwali Comorian"] = "wlc", ["Mwan"] = "moa", ["Mwani"] = "wmw", ["Mwatebu"] = "mwa", ["Mwera"] = "mwe", ["Mwerlap"] = "mrm", ["Mwimbi-Muthambi"] = "mws", ["Mwotlap"] = "mlv", ["Mycenaean Greek"] = "gmy", ["Myene"] = "mye", ["Mysian"] = "yms", ["Mzieme Naga"] = "nme", ["Mághdì"] = "gmd", ["Mòcheno"] = "mhn", ["Mün Chin"] = "mwq", ["Mündü"] = "muh", ["Māori"] = "mi", ["Mạ"] = "cma", ["N'Ko"] = "nqo", ["Na"] = "nbt", ["Naaba"] = "nao", ["Naba"] = "mne", ["Nabak"] = "naf", ["Nabi"] = "mty", ["Nachering"] = "ncd", ["Nadruvian"] = "ndf", ["Nadëb"] = "mbj", ["Nafaanra"] = "nfr", ["Nafi"] = "srf", ["Nafri"] = "nxx", ["Naga Pidgin"] = "nag", ["Nagarchal"] = "nbg", ["Nage"] = "nxe", ["Nagtipunan Agta"] = "phi-nag", ["Nagu"] = "ngr", ["Nagumi"] = "ngv", ["Nahali"] = "nlx", ["Nahari"] = "nhh", ["Nahavaq"] = "sns", ["Nahuatl"] = "nah", ["Nai"] = "bio", ["Najdi Arabic"] = "ars", ["Naka'ela"] = "nae", ["Nakai"] = "nkj", ["Nakame"] = "nib", ["Nakanai"] = "nak", ["Nakara"] = "nck", ["Nake"] = "nbk", ["Naki"] = "mff", ["Nakwi"] = "nax", ["Nalca"] = "nlc", ["Nali"] = "nss", ["Nalik"] = "nal", ["Nalu"] = "naj", ["Nalögo"] = "nlz", ["Nama"] = "nmx", ["Namakura"] = "nmk", ["Namat"] = "nkm", ["Nambikwara"] = "nab", ["Nambo"] = "ncm", ["Nambya"] = "nmq", ["Namia"] = "nnm", ["Namiae"] = "nvm", ["Namibian Sign Language"] = "nbs", ["Namla"] = "naa", ["Namo"] = "mxw", ["Namonuito"] = "nmt", ["Namosi-Naitasiri-Serua"] = "bwb", ["Namuyi"] = "nmy", ["Nanai"] = "gld", ["Nancere"] = "nnc", ["Nande"] = "nnb", ["Nandi"] = "niq", ["Nanerige"] = "sen", ["Nanga Dama Dogon"] = "nzz", ["Nankina"] = "nnk", ["Nanti"] = "cox", ["Nanticoke"] = "nnt", ["Nanubae"] = "afk", ["Naolan"] = "nai-nao", ["Napu"] = "npy", ["Nar Phu"] = "npa", ["Nara"] = "nrb", ["Narak"] = "nac", ["Narango"] = "nrg", ["Narim"] = "loh", ["Naro"] = "nhr", ["Narom"] = "nrm", ["Narragansett"] = "xnt", ["Narua"] = "nru", ["Narungga"] = "nnr", ["Naruo"] = "ylo", ["Nasal"] = "nsy", ["Nasarian"] = "nvh", ["Nasioi"] = "nas", ["Naskapi"] = "nsk", ["Nasu"] = "ywq", ["Natagaimas"] = "nts", ["Natchez"] = "ncz", ["Nateni"] = "ntm", ["Natioro"] = "nti", ["Natú"] = "sai-nat", ["Natügu"] = "ntu", ["Nauete"] = "nxa", ["Naukanski"] = "ynk", ["Nauna"] = "ncn", ["Nauo"] = "nwo", ["Nauruan"] = "na", ["Navajo"] = "nv", ["Navut"] = "nsw", ["Nawaru"] = "nwr", ["Nawathinehena"] = "nwa", ["Nawdm"] = "nmz", ["Nawuri"] = "naw", ["Naxi"] = "nxq", ["Nayi"] = "noz", ["Naʼvi"] = "art-nav", ["Ncane"] = "ncr", ["Nchumbulu"] = "nlu", ["Nda'nda'"] = "nnz", ["Ndai"] = "gke", ["Ndaka"] = "ndk", ["Ndali"] = "ndh", ["Ndam"] = "ndm", ["Ndamba"] = "ndj", ["Ndambomo"] = "nxo", ["Ndasa"] = "nda", ["Ndau"] = "ndc", ["Nde-Gbite"] = "ned", ["Nde-Nsele-Nta"] = "ndd", ["Ndemli"] = "nml", ["Ndendeule"] = "dne", ["Ndengereko"] = "ndg", ["Nding"] = "eli", ["Ndjébbana"] = "djj", ["Ndo"] = "ndp", ["Ndobo"] = "ndw", ["Ndoe"] = "nbb", ["Ndogo"] = "ndz", ["Ndolo"] = "ndl", ["Ndom"] = "nqm", ["Ndombe"] = "ndq", ["Ndonga"] = "ng", ["Ndoola"] = "ndr", ["Ndrulo"] = "dno", ["Nduga"] = "ndx", ["Ndumu"] = "nmd", ["Ndunda"] = "nuh", ["Ndunga"] = "ndt", ["Ndut"] = "ndv", ["Ndyuka-Trio Pidgin"] = "njt", ["Ndzwani Comorian"] = "wni", ["Neapolitan"] = "nap", ["Nedebang"] = "nec", ["Nefamese"] = "nef", ["Nefusa"] = "jbn", ["Negerhollands"] = "dcr", ["Negeri Sembilan Malay"] = "zmi", ["Negidal"] = "neg", ["Nehan"] = "nsn", ["Nek"] = "nif", ["Nekgini"] = "nkg", ["Neko"] = "nej", ["Neku"] = "nek", ["Neme"] = "nex", ["Nemi"] = "nem", ["Nen"] = "nqn", ["Nend"] = "anh", ["Nengone"] = "nen", ["Neo"] = "neu", ["Nepalese Sign Language"] = "nsp", ["Nepali"] = "ne", ["Nese"] = "poz-nes", ["Nete"] = "net", ["Neve'ei"] = "vnm", ["Neverver"] = "lgk", ["New Caledonian Javanese"] = "jas", ["New River Shasta"] = "nai-nrs", ["New Zealand Sign Language"] = "nzs", ["Newar"] = "new", ["Neyo"] = "ney", ["Nez Perce"] = "nez", ["Nga La"] = "hlt", ["Ngaanyatjarra"] = "ntj", ["Ngadha"] = "nxg", ["Ngadjunmaya"] = "nju", ["Ngadjuri"] = "jui", ["Ngaiawang"] = "nwg", ["Ngaing"] = "nnf", ["Ngaju"] = "nij", ["Ngala"] = "nud", ["Ngalakan"] = "nig", ["Ngalkbun"] = "ngk", ["Ngalum"] = "szb", ["Ngam"] = "nmc", ["Ngamambo"] = "nbv", ["Ngambay"] = "sba", ["Ngamini"] = "nmv", ["Ngamo"] = "nbh", ["Ngan'gityemerri"] = "nam", ["Nganakarti"] = "xnk", ["Nganasan"] = "nio", ["Ngandi"] = "nid", ["Ngando (Central African Republic)"] = "ngd", ["Ngando (Congo)"] = "nxd", ["Ngandyera"] = "nne", ["Ngangam"] = "gng", ["Ngantangarra"] = "ntg", ["Nganyaywana"] = "nyx", ["Ngardi"] = "rxd", ["Ngarigu"] = "xni", ["Ngarinman"] = "nbj", ["Ngarinyin"] = "ung", ["Ngarla"] = "nrk", ["Ngarluma"] = "nrl", ["Ngarrindjeri"] = "nay", ["Ngas"] = "anc", ["Ngasa"] = "nsg", ["Ngatik Men's Creole"] = "ngm", ["Ngawn Chin"] = "cnw", ["Ngawun"] = "nxn", ["Ngazidja Comorian"] = "zdj", ["Ngbaka"] = "nga", ["Ngbaka Ma'bo"] = "nbm", ["Ngbaka Manza"] = "ngg", ["Ngbee"] = "jgb", ["Ngbinda"] = "nbd", ["Ngbundu"] = "nuu", ["Ngelima"] = "agh", ["Ngemba"] = "nge", ["Ngen of Djonkro"] = "gnj", ["Ngendelengo"] = "nql", ["Ngeq"] = "ngt", ["Ngete"] = "nnn", ["Nggem"] = "nbq", ["Nggwahyi"] = "ngx", ["Ngie"] = "ngj", ["Ngiemboon"] = "nnh", ["Ngile"] = "jle", ["Ngindo"] = "nnq", ["Ngiti"] = "niy", ["Ngiyambaa"] = "wyb", ["Ngizim"] = "ngi", ["Ngkoth"] = "aus-ngk", ["Ngkâlmpw Kanum"] = "kcd", ["Ngochang"] = "tbq-ngo", ["Ngom"] = "nra", ["Ngomba"] = "jgo", ["Ngombale"] = "nla", ["Ngombe (Central African Republic)"] = "nmj", ["Ngombe (Congo)"] = "ngc", ["Ngong"] = "nnx", ["Ngongo"] = "noq", ["Ngoni"] = "ngo", ["Ngoreme"] = "ngq", ["Ngoshie"] = "nsh", ["Ngulu"] = "ngp", ["Nguluwan"] = "nuw", ["Ngumbi"] = "nui", ["Ngunawal"] = "xul", ["Ngundi"] = "ndn", ["Ngundu"] = "nue", ["Ngungwel"] = "ngz", ["Ngurmbur"] = "nrx", ["Nguôn"] = "nuo", ["Ngwaba"] = "ngw", ["Ngwe"] = "nwe", ["Ngwi"] = "nlo", ["Ngwo"] = "ngn", ["Ngäbere"] = "gym", ["Nhanda"] = "nha", ["Nheengatu"] = "yrl", ["Nhirrpi"] = "hrp", ["Nhuwala"] = "nhf", ["Nias"] = "nia", ["Nicaraguan Creole"] = "bzk", ["Nicaraguan Sign Language"] = "ncs", ["Nicola"] = "ath-nic", ["Nicoleño"] = "azc-nic", ["Niellim"] = "nie", ["Nigeria Mambila"] = "mzk", ["Nigerian Pidgin"] = "pcm", ["Nigerian Sign Language"] = "nsi", ["Nihali"] = "nll", ["Nii"] = "nii", ["Niksek"] = "gbe", ["Nila"] = "nil", ["Nilamba"] = "nim", ["Nimadi"] = "noe", ["Nimanbur"] = "nmp", ["Nimbari"] = "nmr", ["Nimboran"] = "nir", ["Nimi"] = "nis", ["Nimo"] = "niw", ["Nimoa"] = "nmw", ["Ninam"] = "shb", ["Nindi"] = "nxi", ["Ningera"] = "nby", ["Ninggerum"] = "nxr", ["Ningil"] = "niz", ["Ninia Yali"] = "nlk", ["Ninzo"] = "nin", ["Nipsan"] = "nps", ["Nisa"] = "njs", ["Nisenan"] = "nsz", ["Nisga'a"] = "ncg", ["Nisi"] = "yso", ["Niuafo'ou"] = "num", ["Niuatoputapu"] = "nkp", ["Niuean"] = "niu", ["Nivaclé"] = "cag", ["Nivkh"] = "niv", ["Niwer Mil"] = "hrc", ["Niya Prakrit"] = "pra-niy", ["Njalgulgule"] = "njl", ["Njebi"] = "nzb", ["Njen"] = "njj", ["Njerep"] = "njr", ["Njyem"] = "njy", ["Nkami"] = "nkq", ["Nkangala"] = "nkn", ["Nkari"] = "nkz", ["Nkem-Nkum"] = "isi", ["Nkhumbi"] = "khu", ["Nkongho"] = "nkc", ["Nkonya"] = "nko", ["Nkoroo"] = "nkx", ["Nkoya"] = "nka", ["Nkukoli"] = "nbo", ["Nkutu"] = "nkw", ["Nnam"] = "nbp", ["Noakhali"] = "oak", ["Nobiin"] = "fia", ["Nobonob"] = "gaw", ["Nocte"] = "njb", ["Nogai"] = "nog", ["Noipä"] = "npx", ["Noiri"] = "noi", ["Nokuku"] = "nkk", ["Nomaande"] = "lem", ["Nomane"] = "nof", ["Nomatsiguenga"] = "not", ["Nomlaki"] = "nol", ["Nomu"] = "noh", ["Nong Zhuang"] = "zhn", ["Nonuya"] = "noj", ["Nooksack"] = "nok", ["Noon"] = "snf", ["Noone"] = "nhu", ["Nootka"] = "nuk", ["Nopala Chatino"] = "cya", ["Noric"] = "nrc", ["Norman"] = "nrf", ["Norn"] = "nrn", ["Norra"] = "nrr", ["North Ambrym"] = "mmg", ["North Asmat"] = "nks", ["North Awyu"] = "yir", ["North Babar"] = "bcd", ["North Boma"] = "boh", ["North Central Mixe"] = "neq", ["North Efate"] = "llp", ["North Fali"] = "fll", ["North Frisian"] = "frr", ["North Giziga"] = "gis", ["North Levantine Arabic"] = "apc", ["North Marquesan"] = "mrq", ["North Mesopotamian Arabic"] = "ayp", ["North Mofu"] = "mfk", ["North Moluccan Malay"] = "max", ["North Muyu"] = "kti", ["North Nuaulu"] = "nni", ["North Picene"] = "nrp", ["North Slavey"] = "scs", ["North Tairora"] = "tbg", ["North Tanna"] = "tnn", ["North Tukang Besi"] = "khc", ["North Wahgi"] = "whg", ["North Watut"] = "una", ["Northeast Kiwai"] = "kiw", ["Northeast Maidu"] = "nmu", ["Northeast Malakula"] = "upv", ["Northeast Pashayi"] = "aee", ["Northeastern Dinka"] = "dip", ["Northeastern Pomo"] = "pef", ["Northern Alta"] = "aqn", ["Northern Altai"] = "atv", ["Northern Amami Ōshima"] = "ryn", ["Northern Bontoc"] = "rbk", ["Northern Catanduanes Bikol"] = "cts", ["Northern Dagara"] = "dgi", ["Northern East Cree"] = "crl", ["Northern Emberá"] = "emp", ["Northern Ghale"] = "ghh", ["Northern Grebo"] = "gbo", ["Northern Guiyang Hmong"] = "huj", ["Northern Haida"] = "hdn", ["Northern Hindko"] = "hno", ["Northern Huishui Hmong"] = "hmi", ["Northern Kalapuya"] = "nrt", ["Northern Kam"] = "doc", ["Northern Khanty"] = "kca-nor", ["Northern Khmer"] = "kxm", ["Northern Kissi"] = "kqs", ["Northern Kurdish"] = "kmr", ["Northern Lorung"] = "lbr", ["Northern Luri"] = "lrc", ["Northern Mansi"] = "mns-nor", ["Northern Mashan Hmong"] = "hmp", ["Northern Min"] = "mnp", ["Northern Muji"] = "ymx", ["Northern Ndebele"] = "nd", ["Northern Ngbandi"] = "ngb", ["Northern Nisu"] = "yiv", ["Northern Nuni"] = "nuv", ["Northern Oaxaca Nahuatl"] = "nhy", ["Northern Ohlone"] = "cst", ["Northern One"] = "onr", ["Northern Paiute"] = "pao", ["Northern Pame"] = "pmq", ["Northern Pinghua"] = "cnp", ["Northern Pomo"] = "pej", ["Northern Puebla Nahuatl"] = "ncj", ["Northern Pumi"] = "pmi", ["Northern Pwo"] = "pww", ["Northern Qiandong Miao"] = "hea", ["Northern Qiang"] = "cng", ["Northern Rengma Naga"] = "nnl", ["Northern Roglai"] = "rog", ["Northern Saharan Berber"] = "mzb", ["Northern Sami"] = "se", ["Northern Selkup"] = "sel-nor", ["Northern Sierra Miwok"] = "nsq", ["Northern Sotho"] = "nso", ["Northern Subanen"] = "stb", ["Northern Tarahumara"] = "thh", ["Northern Tepehuan"] = "ntp", ["Northern Thai"] = "nod", ["Northern Tidung"] = "ntd", ["Northern Tlaxiaco Mixtec"] = "xtn", ["Northern Toussian"] = "tsp", ["Northern Tujia"] = "tji", ["Northern Tutchone"] = "ttm", ["Northern Valley Yokuts"] = "yok-nvy", ["Northern Yukaghir"] = "ykg", ["Northwest Gbaya"] = "gya", ["Northwest Maidu"] = "mjd", ["Northwest Oaxaca Mixtec"] = "mxa", ["Northwest Pashayi"] = "glh", ["Northwestern Dinka"] = "diw", ["Northwestern Fars"] = "faz", ["Northwestern Nisu"] = "nsf", ["Northwestern Ojibwa"] = "ojb", ["Norwegian"] = "no", ["Norwegian Bokmål"] = "nb", ["Norwegian Nynorsk"] = "nn", ["Norwegian Sign Language"] = "nsl", ["Notre"] = "bly", ["Notsi"] = "ncf", ["Nottoway"] = "ntw", ["Nottoway-Meherrin"] = "nwy", ["Novial"] = "nov", ["Noy"] = "noy", ["Nsari"] = "asj", ["Nsenga"] = "nse", ["Nshi"] = "nsc", ["Nsong"] = "soo", ["Nsongo"] = "nsx", ["Ntcham"] = "bud", ["Ntomba"] = "nto", ["Ntra'ngith"] = "dgt", ["Nubaca"] = "baf", ["Nubi"] = "kcn", ["Nuer"] = "nus", ["Nuguria"] = "nur", ["Nuk"] = "noc", ["Nukak Makú"] = "mbr", ["Nukna"] = "klt", ["Nukuini"] = "nuc", ["Nukumanu"] = "nuq", ["Nukunu"] = "nnv", ["Nukunul"] = "xnu", ["Nukuoro"] = "nkr", ["Numana"] = "nbr", ["Numanggang"] = "nop", ["Numbami"] = "sij", ["Nume"] = "tgs", ["Numidian"] = "nxm", ["Numèè"] = "kdk", ["Nungali"] = "nug", ["Nunggubuyu"] = "nuy", ["Nungon"] = "paa-nun", ["Nungu"] = "rin", ["Nuosu"] = "ii", ["Nupbikha"] = "npb", ["Nupe"] = "nup", ["Nusa Laut"] = "nul", ["Nusu"] = "nuf", ["Nutabe"] = "cba-nut", ["Nuxalk"] = "blc", ["Nyabwa"] = "nwb", ["Nyagrong Minyag"] = "ero-nya", ["Nyah Kur"] = "cbn", ["Nyaheun"] = "nev", ["Nyakyusa"] = "nyy", ["Nyali"] = "nlj", ["Nyam"] = "nmi", ["Nyamal"] = "nly", ["Nyambo"] = "now", ["Nyamusa-Molo"] = "nwm", ["Nyamwanga"] = "mwn", ["Nyamwezi"] = "nym", ["Nyaneka"] = "nyk", ["Nyang'i"] = "nyp", ["Nyanga (Congo)"] = "nyj", ["Nyanga (Togo)"] = "ayg", ["Nyanga-li"] = "nyc", ["Nyangatom"] = "nnj", ["Nyangbo"] = "nyb", ["Nyangga"] = "nny", ["Nyangumarta"] = "nna", ["Nyankole"] = "nyn", ["Nyarafolo"] = "sev", ["Nyaturu"] = "rim", ["Nyaw"] = "nyw", ["Nyawaygi"] = "nyt", ["Nyelâyu"] = "yly", ["Nyemba"] = "nba", ["Nyengo"] = "nye", ["Nyenkha"] = "neh", ["Nyeu"] = "nyl", ["Nyigina"] = "nyh", ["Nyiha"] = "nih", ["Nyika"] = "nkt", ["Nyimang"] = "nyi", ["Nyindrou"] = "lid", ["Nyindu"] = "nyg", ["Nyishi"] = "njz", ["Nyiyaparli"] = "xny", ["Nyokon"] = "nvo", ["Nyole (Kenya)"] = "nyd", ["Nyole (Uganda)"] = "nuj", ["Nyong"] = "muo", ["Nyoro"] = "nyo", ["Nyulnyul"] = "nyv", ["Nyunga"] = "nys", ["Nyungwe"] = "nyu", ["Nzadi"] = "nzd", ["Nzakambay"] = "nzy", ["Nzakara"] = "nzk", ["Nzanyi"] = "nja", ["Nzima"] = "nzi", ["Ná-Meo"] = "neo", ["Nùng"] = "nut", ["Nüpode Huitoto"] = "hux", ["Nǀuu"] = "ngh", ["O'chi'chi'"] = "xoc", ["O'du"] = "tyh", ["O'odham"] = "ood", ["Obanliku"] = "bzy", ["Obispeño"] = "obi", ["Oblo"] = "obl", ["Obo Manobo"] = "obo", ["Obokuitai"] = "afz", ["Obolo"] = "ann", ["Obulom"] = "obu", ["Ocaina"] = "oca", ["Occitan"] = "oc", ["Ocotepec Mixtec"] = "mie", ["Ocotlán Zapotec"] = "zac", ["Od"] = "odk", ["Odia"] = "or", ["Odiai"] = "bhf", ["Odoodee"] = "kkc", ["Odual"] = "odu", ["Odut"] = "oda", ["Ofayé"] = "opy", ["Ofo"] = "ofo", ["Ogbah"] = "ogc", ["Ogbia"] = "ogb", ["Ogbogolo"] = "ogg", ["Ogbronuagum"] = "ogu", ["Ogea"] = "eri", ["Oirata"] = "oia", ["Ojibwe"] = "oj", ["Ojitlán Chinantec"] = "chj", ["Okanagan"] = "oka", ["Okiek"] = "oki", ["Okinawan"] = "ryu", ["Okinoerabu"] = "okn", ["Oko-Eni-Osayen"] = "oks", ["Oko-Juwoi"] = "okj", ["Okobo"] = "okb", ["Okodia"] = "okd", ["Okolod"] = "kqv", ["Okpamheri"] = "opa", ["Okpe (Northwestern Edo)"] = "okx", ["Okpe (Southwestern Edo)"] = "oke", ["Okpela"] = "atg", ["Oksapmin"] = "opm", ["Oku"] = "oku", ["Okwanuchu"] = "nai-okw", ["Old Anatolian Turkish"] = "trk-oat", ["Old Armenian"] = "xcl", ["Old Avar"] = "oav", ["Old Awadhi"] = "inc-oaw", ["Old Bengali"] = "inc-obn", ["Old Breton"] = "obt", ["Old Burmese"] = "obr", ["Old Catalan"] = "roa-oca", ["Old Cham"] = "ocm", ["Old Chinese"] = "och", ["Old Church Slavonic"] = "cu", ["Old Cornish"] = "oco", ["Old Czech"] = "zlw-ocs", ["Old Danish"] = "gmq-oda", ["Old Dutch"] = "odt", ["Old East Slavic"] = "orv", ["Old English"] = "ang", ["Old French"] = "fro", ["Old Frisian"] = "ofs", ["Old Galician-Portuguese"] = "roa-opt", ["Old Georgian"] = "oge", ["Old Gujarati"] = "inc-ogu", ["Old Gutnish"] = "gmq-ogt", ["Old High German"] = "goh", ["Old Hindi"] = "inc-ohi", ["Old Hungarian"] = "ohu", ["Old Irish"] = "sga", ["Old Japanese"] = "ojp", ["Old Javanese"] = "kaw", ["Old Kannada"] = "dra-okn", ["Old Kentish Sign Language"] = "okl", ["Old Khmer"] = "okz", ["Old Komi"] = "urj-koo", ["Old Korean"] = "oko", ["Old Leonese"] = "roa-ole", ["Old Lithuanian"] = "olt", ["Old Manipuri"] = "omp", ["Old Marathi"] = "omr", ["Old Median"] = "xme-old", ["Old Mon"] = "omx", ["Old Navarro-Aragonese"] = "roa-ona", ["Old Norse"] = "non", ["Old Novgorodian"] = "zle-ono", ["Old Nubian"] = "onw", ["Old Occitan"] = "pro", ["Old Odia"] = "inc-oor", ["Old Persian"] = "peo", ["Old Polish"] = "zlw-opl", ["Old Prussian"] = "prg", ["Old Punjabi"] = "inc-opa", ["Old Ruthenian"] = "zle-ort", ["Old Saxon"] = "osx", ["Old Slovak"] = "zlw-osk", ["Old South Arabian"] = "sem-srb", ["Old Spanish"] = "osp", ["Old Sundanese"] = "osn", ["Old Swedish"] = "gmq-osw", ["Old Tamil"] = "oty", ["Old Tati"] = "xme-ott", ["Old Telugu"] = "dra-ote", ["Old Tibetan"] = "otb", ["Old Tupi"] = "tpw", ["Old Turkic"] = "otk", ["Old Uyghur"] = "oui", ["Old Warekena"] = "awd-war", ["Old Welsh"] = "owl", ["Olekha"] = "ole", ["Ollari"] = "gdb", ["Olo"] = "ong", ["Oloma"] = "olm", ["Olrat"] = "olr", ["Olu'bo"] = "lul", ["Olukumi"] = "ulb", ["Olulumo-Ikom"] = "iko", ["Oluta Popoluca"] = "plo", ["Olutsotso"] = "lto", ["Omagua"] = "omg", ["Omaha-Ponca"] = "oma", ["Omani Arabic"] = "acx", ["Omba"] = "omb", ["Ombamba"] = "mbm", ["Ombo"] = "oml", ["Ometepec Nahuatl"] = "nht", ["Omi"] = "omi", ["Omok"] = "omk", ["Omotik"] = "omt", ["Omurano"] = "omu", ["Oneida"] = "one", ["Ong"] = "oog", ["Ongota"] = "bxe", ["Onin"] = "oni", ["Onjob"] = "onj", ["Ono"] = "ons", ["Onobasulu"] = "onn", ["Onondaga"] = "ono", ["Ontong Java"] = "ojv", ["Oorlams"] = "oor", ["Opao"] = "opo", ["Opata"] = "opt", ["Opuuo"] = "lgn", ["Opón"] = "sai-opo", ["Oraon Sadri"] = "sdr", ["Orejón"] = "ore", ["Oring"] = "org", ["Orizaba Nahuatl"] = "nlv", ["Orléanais"] = "roa-orl", ["Ormu"] = "orz", ["Ormuri"] = "oru", ["Oro"] = "orx", ["Oro Win"] = "orw", ["Oroch"] = "oac", ["Oroha"] = "ora", ["Orok"] = "oaa", ["Orokaiva"] = "okv", ["Oroko"] = "bdu", ["Orokolo"] = "oro", ["Oromo"] = "om", ["Oroqen"] = "orh", ["Orowe"] = "bpk", ["Oruma"] = "orr", ["Orya"] = "ury", ["Osage"] = "osa", ["Osamayi"] = "syx", ["Osatu"] = "ost", ["Oscan"] = "osc", ["Osing"] = "osi", ["Ososo"] = "oso", ["Ossetian"] = "os", ["Ot Danum"] = "otd", ["Otank"] = "uta", ["Oti"] = "oti", ["Otomaco"] = "sai-oto", ["Otoro"] = "otr", ["Ottawa"] = "otw", ["Ottoman Turkish"] = "ota", ["Otuke"] = "otu", ["Ouma"] = "oum", ["Oune"] = "oue", ["Owa"] = "stn", ["Owenia"] = "wsr", ["Owiniga"] = "owi", ["Oy"] = "oyb", ["Oya'oya"] = "oyy", ["Oyda"] = "oyd", ["Ozolotepec Zapotec"] = "zao", ["Ozumacín Chinantec"] = "chz", ["Pa"] = "ppt", ["Pa Di"] = "pdi", ["Pa'a"] = "pqa", ["Pa'o Karen"] = "blk", ["Pa-Hng"] = "pha", ["Paamese"] = "pma", ["Paasaal"] = "sig", ["Pacahuara"] = "pcp", ["Pacoh"] = "pac", ["Padoe"] = "pdo", ["Paelignian"] = "pgn", ["Paeonian"] = "ine-pae", ["Pagi"] = "pgi", ["Pagibete"] = "pae", ["Pagu"] = "pgu", ["Pahanan Agta"] = "apf", ["Pahari Newari"] = "phj", ["Pahari-Potwari"] = "phr", ["Pahi"] = "lgt", ["Pahlavani"] = "phv", ["Pai Tavytera"] = "pta", ["Paicî"] = "pri", ["Paikoneka"] = "awd-pai", ["Paipai"] = "ppi", ["Paite"] = "pck", ["Paiwan"] = "pwn", ["Pajapan Nahuatl"] = "nhp", ["Pajonal Ashéninka"] = "cjo", ["Pak-Tong"] = "pkg", ["Pakanha"] = "pkn", ["Pakistan Sign Language"] = "pks", ["Paku"] = "pku", ["Paku Karen"] = "jkp", ["Pal"] = "abw", ["Palaic"] = "plq", ["Palaka"] = "plr", ["Palantla Chinantec"] = "cpa", ["Palauan"] = "pau", ["Palawan Batak"] = "bya", ["Paleni"] = "pnl", ["Palenquero"] = "pln", ["Palewyami Yokuts"] = "yok-ply", ["Pali"] = "pi", ["Palikur"] = "plu", ["Paliyan"] = "pcf", ["Pallanganmiddang"] = "pmd", ["Palor"] = "fap", ["Palta"] = "sai-pal", ["Palu'e"] = "ple", ["Paluan"] = "plz", ["Palula"] = "phl", ["Palya Bareli"] = "bpx", ["Pam"] = "pmn", ["Pambia"] = "pmb", ["Pamigua"] = "sai-pam", ["Pamlico"] = "pmk", ["Pamona"] = "pmf", ["Pamosu"] = "hih", ["Pamplona Atta"] = "att", ["Pana (Central Africa)"] = "pnz", ["Pana (West Africa)"] = "pnq", ["Panamanian Sign Language"] = "lsp", ["Panamint"] = "par", ["Panare"] = "pbh", ["Panará"] = "kre", ["Panasuan"] = "psn", ["Panawa"] = "pwb", ["Pancana"] = "pnp", ["Panchpargania"] = "tdb", ["Pande"] = "bkj", ["Pangasinan"] = "pag", ["Pangseng"] = "pgs", ["Pangutaran Sama"] = "slm", ["Pangwa"] = "pbr", ["Pangwali"] = "pgg", ["Panim"] = "pnr", ["Paniya"] = "pcg", ["Pankararé"] = "pax", ["Pankararú"] = "paz", ["Pankhu"] = "pkh", ["Pannei"] = "pnc", ["Pannonian Rusyn"] = "rsk", ["Panobo"] = "pno", ["Panyi Bai"] = "bfc", ["Panyjima"] = "pnw", ["Panzaleo"] = "sai-pnz", ["Pao"] = "ppa", ["Papantla Totonac"] = "top", ["Papapana"] = "ppn", ["Papar"] = "dpp", ["Papasena"] = "pas", ["Papel"] = "pbo", ["Papi"] = "ppe", ["Papiamentu"] = "pap", ["Papora"] = "ppu", ["Papua New Guinean Sign Language"] = "pgz", ["Papuan Malay"] = "pmy", ["Papuma"] = "ppm", ["Para Naga"] = "pzn", ["Parachi"] = "prc", ["Paraguayan Guarani"] = "gug", ["Paraguayan Sign Language"] = "pys", ["Parakanã"] = "pak", ["Paranan"] = "prf", ["Paranawát"] = "paf", ["Paratió"] = "sai-par", ["Paraujano"] = "pbg", ["Parauk"] = "prk", ["Parawen"] = "prw", ["Pardhan"] = "pch", ["Pardhi"] = "pcl", ["Pare"] = "asa", ["Pareci"] = "pab", ["Paredarerme"] = "xpd", ["Parenga"] = "pcj", ["Parkari Koli"] = "kvx", ["Parthian"] = "xpr", ["Parya"] = "paq", ["Pará Gavião"] = "gvp", ["Pashto"] = "ps", ["Pasi"] = "psq", ["Pass Valley Yali"] = "yac", ["Pasé"] = "awd-pas", ["Patagón"] = "sai-ptg", ["Patamona"] = "pbc", ["Patani"] = "ptn", ["Pataxó"] = "sai-ptx", ["Pataxó Hã-Hã-Hãe"] = "pth", ["Patep"] = "ptp", ["Pathiya"] = "pty", ["Patpatar"] = "gfk", ["Pattani"] = "lae", ["Pattani Malay"] = "mfa", ["Pattapu"] = "ptq", ["Patwin"] = "pwi", ["Paulohi"] = "plh", ["Paumarí"] = "pad", ["Paunaka"] = "pnk", ["Pauri Bareli"] = "bfb", ["Pauserna"] = "psm", ["Pawaia"] = "pwa", ["Pawnee"] = "paw", ["Payaguá"] = "sai-pyg", ["Pazeh"] = "pzh", ["Pe"] = "pai", ["Pear"] = "pcb", ["Peba"] = "sai-peb", ["Pech"] = "pay", ["Pecheneg"] = "xpc", ["Peerapper"] = "xpw", ["Peere"] = "pfe", ["Pei"] = "ppq", ["Pekal"] = "pel", ["Pela"] = "bxd", ["Pele-Ata"] = "ata", ["Pemon"] = "aoc", ["Penang Sign Language"] = "psg", ["Penchal"] = "pek", ["Pendau"] = "ums", ["Pengo"] = "peg", ["Pennsylvania German"] = "pdc", ["Penobscot"] = "aaq", ["Penrhyn"] = "pnh", ["Pentlatch"] = "ptw", ["Perai"] = "wet", ["Peranakan Indonesian"] = "pea", ["Perema"] = "wom", ["Perené Ashéninka"] = "prq", ["Pericú"] = "nai-per", ["Pero"] = "pip", ["Persian"] = "fa", ["Persian Sign Language"] = "psc", ["Peruvian Sign Language"] = "prl", ["Petapa Zapotec"] = "zpe", ["Petats"] = "pex", ["Petjo"] = "pey", ["Peñoles Mixtec"] = "mil", ["Phai"] = "prt", ["Phake"] = "phk", ["Phala"] = "ypa", ["Phana'"] = "phq", ["Phangduwali"] = "phw", ["Phende"] = "pem", ["Philippine Sign Language"] = "psp", ["Philistine"] = "mis-phi", ["Phimbi"] = "phm", ["Phoenician"] = "phn", ["Phola"] = "ypg", ["Pholo"] = "yip", ["Phom"] = "nph", ["Phong-Kniang"] = "pnx", ["Phrae Pwo"] = "kjt", ["Phrygian"] = "xpg", ["Phu Thai"] = "pht", ["Phuan"] = "phu", ["Phudagi"] = "phd", ["Phuie"] = "pug", ["Phukha"] = "phh", ["Phuma"] = "ypm", ["Phunoi"] = "pho", ["Phuong"] = "phg", ["Phupa"] = "ypp", ["Phupha"] = "yph", ["Phuthi"] = "bnt-phu", ["Phuza"] = "ypz", ["Piamatsina"] = "ptr", ["Piame"] = "pin", ["Piapoco"] = "pio", ["Piaroa"] = "pid", ["Picard"] = "pcd", ["Pichinglis"] = "fpe", ["Pichis Ashéninka"] = "cpu", ["Pictish"] = "xpi", ["Picuris"] = "nai-pic", ["Pidgin Delaware"] = "dep", ["Pidgin Iha"] = "ihb", ["Pidgin Onin"] = "onx", ["Piedmontese"] = "pms", ["Pijao"] = "pij", ["Pije"] = "piz", ["Pijin"] = "pis", ["Pilagá"] = "plg", ["Pileni"] = "piv", ["Pima Bajo"] = "pia", ["Pimbwe"] = "piw", ["Pinai-Hagahai"] = "pnn", ["Pingelapese"] = "pif", ["Pinigura"] = "pnv", ["Pinjarup"] = "pnj", ["Pinji"] = "pic", ["Pinotepa Nacional Mixtec"] = "mio", ["Pintiini"] = "pti", ["Pintupi-Luritja"] = "piu", ["Pinyin"] = "pny", ["Pipil"] = "ppl", ["Pirahã"] = "myp", ["Piratapuyo"] = "pir", ["Pirlatapa"] = "bxi", ["Piro"] = "pie", ["Pirriya"] = "xpa", ["Pisabo"] = "pig", ["Pisaflores Tepehua"] = "tpp", ["Piscataway"] = "psy", ["Pisidian"] = "xps", ["Pitcairn-Norfolk"] = "pih", ["Pite Sami"] = "sje", ["Piti"] = "pcn", ["Pitjantjatjara"] = "pjt", ["Pitta-Pitta"] = "pit", ["Piu"] = "pix", ["Piya-Kwonci"] = "piy", ["Plains Apache"] = "apk", ["Plains Cree"] = "crk", ["Plains Indian Sign Language"] = "psd", ["Plains Miwok"] = "pmw", ["Plapo Krumen"] = "ktj", ["Plautdietsch"] = "pdt", ["Playero"] = "gob", ["Pnar"] = "pbv", ["Pochuri Naga"] = "npo", ["Pochutec"] = "xpo", ["Podoko"] = "pbi", ["Pogali"] = "hkh", ["Pogolo"] = "poy", ["Pohnpeian"] = "pon", ["Poitevin-Saintongeais"] = "roa-poi", ["Pokangá"] = "pok", ["Poke"] = "pof", ["Pol"] = "pmm", ["Polabian"] = "pox", ["Polci"] = "plj", ["Polish"] = "pl", ["Polish Sign Language"] = "pso", ["Polonombauk"] = "plb", ["Pom"] = "pmo", ["Ponam"] = "ncc", ["Pondi"] = "lnm", ["Pongu"] = "png", ["Ponosakan"] = "pns", ["Pontic Greek"] = "pnt", ["Ponyo"] = "npg", ["Poqomam"] = "poc", ["Poqomchi'"] = "poh", ["Porohanon"] = "prh", ["Port Sandwich"] = "psw", ["Port Sorell"] = "xpl", ["Port Vato"] = "ptv", ["Portuguese"] = "pt", ["Portuguese Sign Language"] = "psr", ["Potawatomi"] = "pot", ["Potiguára"] = "pog", ["Poumei Naga"] = "pmx", ["Pouye"] = "bye", ["Powari"] = "pwr", ["Powhatan"] = "pim", ["Poyanáwa"] = "pyn", ["Prakrit"] = "pra", ["Prasuni"] = "prn", ["Pre-Samnite"] = "itc-psa", ["Primitive Irish"] = "pgl", ["Principense"] = "pre", ["Proto-Abkhaz-Abaza"] = "cau-abz-pro", ["Proto-Afroasiatic"] = "afa-pro", ["Proto-Albanian"] = "sqj-pro", ["Proto-Algic"] = "aql-pro", ["Proto-Algonquian"] = "alg-pro", ["Proto-Amuesha-Chamicuro"] = "awd-amc-pro", ["Proto-Anatolian"] = "ine-ana-pro", ["Proto-Andian"] = "cau-and-pro", ["Proto-Apachean"] = "apa-pro", ["Proto-Arawak"] = "awd-pro", ["Proto-Arawan"] = "auf-pro", ["Proto-Armenian"] = "hyx-pro", ["Proto-Arnhem"] = "aus-arn-pro", ["Proto-Aroid"] = "omv-aro-pro", ["Proto-Aslian"] = "mkh-asl-pro", ["Proto-Atayalic"] = "map-ata-pro", ["Proto-Athabaskan"] = "ath-pro", ["Proto-Atlantic-Congo"] = "alv-pro", ["Proto-Austroasiatic"] = "aav-pro", ["Proto-Austronesian"] = "map-pro", ["Proto-Avaro-Andian"] = "cau-ava-pro", ["Proto-Bahnaric"] = "mkh-ban-pro", ["Proto-Bai"] = "sit-bai-pro", ["Proto-Balto-Slavic"] = "ine-bsl-pro", ["Proto-Bantoid"] = "nic-bod-pro", ["Proto-Bantu"] = "bnt-pro", ["Proto-Basque"] = "euq-pro", ["Proto-Batak"] = "btk-pro", ["Proto-Be"] = "qfa-onb-pro", ["Proto-Be-Tai"] = "qfa-bet-pro", ["Proto-Benue-Congo"] = "nic-bco-pro", ["Proto-Berber"] = "ber-pro", ["Proto-Binanderean"] = "ngf-bin-pro", ["Proto-Bodish"] = "sit-bdi-pro", ["Proto-Bodo-Garo"] = "tbq-bdg-pro", ["Proto-Bongo-Bagirmi"] = "csu-bba-pro", ["Proto-Boran"] = "sai-bor-pro", ["Proto-Brythonic"] = "cel-bry-pro", ["Proto-Bua"] = "alv-bua-pro", ["Proto-Bungku-Tolaki"] = "poz-btk-pro", ["Proto-Burmish"] = "tbq-brm-pro", ["Proto-Caddoan"] = "cdd-pro", ["Proto-Cangin"] = "alv-cng-pro", ["Proto-Cariban"] = "sai-car-pro", ["Proto-Celtic"] = "cel-pro", ["Proto-Central Chadic"] = "cdc-cbm-pro", ["Proto-Central Dravidian"] = "dra-cen-pro", ["Proto-Central Jê"] = "sai-cje-pro", ["Proto-Central Naga"] = "sit-aao-pro", ["Proto-Central New South Wales"] = "aus-cww-pro", ["Proto-Central Pacific"] = "poz-pcc-pro", ["Proto-Central Sudanic"] = "csu-pro", ["Proto-Central Togo"] = "alv-gtm-pro", ["Proto-Central-Eastern Malayo-Polynesian"] = "poz-cet-pro", ["Proto-Cerrado"] = "sai-cer-pro", ["Proto-Chadic"] = "cdc-pro", ["Proto-Chamic"] = "cmc-pro", ["Proto-Chatino"] = "omq-cha-pro", ["Proto-Chibchan"] = "cba-pro", ["Proto-Chimakuan"] = "chi-pro", ["Proto-Chinookan"] = "nai-ckn-pro", ["Proto-Chukotko-Kamchatkan"] = "qfa-cka-pro", ["Proto-Chumash"] = "nai-chu-pro", ["Proto-Circassian"] = "cau-cir-pro", ["Proto-Cupan"] = "azc-cup-pro", ["Proto-Cushitic"] = "cus-pro", ["Proto-Daju"] = "sdv-daj-pro", ["Proto-Daly"] = "aus-dal-pro", ["Proto-Dangari"] = "inc-dng-pro", ["Proto-Dargwa"] = "cau-drg-pro", ["Proto-Dizoid"] = "omv-diz-pro", ["Proto-Dravidian"] = "dra-pro", ["Proto-Eastern Jebel"] = "sdv-eje-pro", ["Proto-Eastern Malayo-Polynesian"] = "pqe-pro", ["Proto-Eastern Oti-Volta"] = "nic-eov-pro", ["Proto-Eastern Polynesian"] = "poz-pep-pro", ["Proto-Edekiri"] = "alv-edk-pro", ["Proto-Edoid"] = "alv-edo-pro", ["Proto-Ersuic"] = "sit-ers-pro", ["Proto-Eskimo"] = "esx-esk-pro", ["Proto-Eskimo-Aleut"] = "esx-pro", ["Proto-Fali"] = "alv-fli-pro", ["Proto-Finnic"] = "urj-fin-pro", ["Proto-Gbaya"] = "gba-pro", ["Proto-Gbe"] = "alv-gbe-pro", ["Proto-Georgian-Zan"] = "ccs-gzn-pro", ["Proto-Germanic"] = "gem-pro", ["Proto-Grassfields"] = "nic-grf-pro", ["Proto-Great Andamanese"] = "qfa-adm-pro", ["Proto-Guang"] = "alv-gng-pro", ["Proto-Gur"] = "nic-gur-pro", ["Proto-Gurunsi"] = "nic-gns-pro", ["Proto-Halmahera-Cenderawasih"] = "poz-hce-pro", ["Proto-Heiban"] = "alv-hei-pro", ["Proto-Hellenic"] = "grk-pro", ["Proto-Highland East Cushitic"] = "cus-hec-pro", ["Proto-Hlai"] = "qfa-lic-pro", ["Proto-Hmong-Mien"] = "hmx-pro", ["Proto-Hmongic"] = "hmn-pro", ["Proto-Hrusish"] = "sit-hrs-pro", ["Proto-Hurro-Urartian"] = "qfa-hur-pro", ["Proto-Idomoid"] = "alv-ido-pro", ["Proto-Igboid"] = "alv-igb-pro", ["Proto-Ijoid"] = "ijo-pro", ["Proto-Indo-Aryan"] = "inc-pro", ["Proto-Indo-European"] = "ine-pro", ["Proto-Indo-Iranian"] = "iir-pro", ["Proto-Inuit"] = "esx-inu-pro", ["Proto-Iranian"] = "ira-pro", ["Proto-Iroquoian"] = "iro-pro", ["Proto-Italic"] = "itc-pro", ["Proto-Iwaidjan"] = "aus-wdj-pro", ["Proto-Japonic"] = "jpx-pro", ["Proto-Jukunoid"] = "nic-jkn-pro", ["Proto-Jê"] = "sai-jee-pro", ["Proto-Kadu"] = "qfa-kad-pro", ["Proto-Kalamian"] = "phi-kal-pro", ["Proto-Kalapuyan"] = "nai-klp-pro", ["Proto-Kam-Sui"] = "qfa-kms-pro", ["Proto-Kampa"] = "awd-kmp-pro", ["Proto-Kamta"] = "inc-krd-pro", ["Proto-Karen"] = "kar-pro", ["Proto-Kartvelian"] = "ccs-pro", ["Proto-Katuic"] = "mkh-kat-pro", ["Proto-Kham"] = "sit-kha-pro", ["Proto-Khanty"] = "kca-pro", ["Proto-Khasian"] = "aav-khs-pro", ["Proto-Khmeric"] = "mkh-kmr-pro", ["Proto-Khmuic"] = "mkh-khm-pro", ["Proto-Kho-Bwa"] = "sit-khb-pro", ["Proto-Khoe"] = "khi-kho-pro", ["Proto-Koman"] = "ssa-kom-pro", ["Proto-Komisenian"] = "ira-kms-pro", ["Proto-Koreanic"] = "qfa-kor-pro", ["Proto-Kra"] = "qfa-kra-pro", ["Proto-Kra-Dai"] = "qfa-tak-pro", ["Proto-Kru"] = "kro-pro", ["Proto-Kuki-Chin"] = "tbq-kuk-pro", ["Proto-Kuliak"] = "ssa-klk-pro", ["Proto-Kurdish"] = "ku-pro", ["Proto-Kwa"] = "alv-kwa-pro", ["Proto-Lalo"] = "tbq-lal-pro", ["Proto-Lampungic"] = "poz-lgx-pro", ["Proto-Lezghian"] = "cau-lzg-pro", ["Proto-Lolo-Burmese"] = "tbq-lob-pro", ["Proto-Loloish"] = "tbq-lol-pro", ["Proto-Lower Cross River"] = "nic-lcr-pro", ["Proto-Luish"] = "sit-luu-pro", ["Proto-Maidun"] = "nai-mdu-pro", ["Proto-Malayic"] = "poz-mly-pro", ["Proto-Malayo-Chamic"] = "poz-mcm-pro", ["Proto-Malayo-Polynesian"] = "poz-pro", ["Proto-Malayo-Sumbawan"] = "poz-msa-pro", ["Proto-Mande"] = "dmn-pro", ["Proto-Mangbetu"] = "csu-maa-pro", ["Proto-Mansi"] = "mns-pro", ["Proto-Mari"] = "chm-pro", ["Proto-Masa"] = "cdc-mas-pro", ["Proto-Mayan"] = "myn-pro", ["Proto-Mazatec"] = "omq-maz-pro", ["Proto-Medo-Parthian"] = "ira-mpr-pro", ["Proto-Micronesian"] = "poz-mic-pro", ["Proto-Mienic"] = "hmx-mie-pro", ["Proto-Min"] = "zhx-min-pro", ["Proto-Mixe-Zoquean"] = "nai-miz-pro", ["Proto-Mixtec"] = "omq-mxt-pro", ["Proto-Mixtecan"] = "omq-mix-pro", ["Proto-Mon-Khmer"] = "mkh-pro", ["Proto-Mongolic"] = "xgn-pro", ["Proto-Monic"] = "mkh-mnc-pro", ["Proto-Mordvinic"] = "urj-mdv-pro", ["Proto-Mumuye"] = "alv-mum-pro", ["Proto-Munda"] = "mun-pro", ["Proto-Munji-Yidgha"] = "ira-mny-pro", ["Proto-Muskogean"] = "nai-mus-pro", ["Proto-Na-Dene"] = "xnd-pro", ["Proto-Nahuan"] = "azc-nah-pro", ["Proto-Naish"] = "sit-nas-pro", ["Proto-Nakh"] = "cau-nkh-pro", ["Proto-Nawiki"] = "awd-nwk-pro", ["Proto-Nguni"] = "bnt-ngu-pro", ["Proto-Nicobarese"] = "aav-nic-pro", ["Proto-Niger-Congo"] = "nic-pro", ["Proto-Nilo-Saharan"] = "ssa-pro", ["Proto-Nilotic"] = "sdv-nil-pro", ["Proto-Norse"] = "gmq-pro", ["Proto-North Dravidian"] = "dra-nor-pro", ["Proto-North Halmahera"] = "paa-nha-pro", ["Proto-North Iroquoian"] = "iro-nor-pro", ["Proto-North Sarawak"] = "poz-swa-pro", ["Proto-Northeast Caucasian"] = "cau-nec-pro", ["Proto-Northern Jê"] = "sai-nje-pro", ["Proto-Northern Naga"] = "sit-kon-pro", ["Proto-Northwest Caucasian"] = "cau-nwc-pro", ["Proto-Nubian"] = "nub-pro", ["Proto-Nuclear Polynesian"] = "poz-pnp-pro", ["Proto-Numic"] = "azc-num-pro", ["Proto-Nupoid"] = "alv-nup-pro", ["Proto-Nuristani"] = "iir-nur-pro", ["Proto-Nyima"] = "sdv-nyi-pro", ["Proto-Nyulnyulan"] = "aus-nyu-pro", ["Proto-Oceanic"] = "poz-oce-pro", ["Proto-Ogoni"] = "nic-ogo-pro", ["Proto-Omotic"] = "omv-pro", ["Proto-Ongan"] = "qfa-ong-pro", ["Proto-Oti-Volta"] = "nic-ovo-pro", ["Proto-Oto-Manguean"] = "omq-pro", ["Proto-Oto-Pamean"] = "omq-otp-pro", ["Proto-Otomi"] = "oto-otm-pro", ["Proto-Otomian"] = "oto-pro", ["Proto-Pakanic"] = "mkh-pkn-pro", ["Proto-Palaungic"] = "mkh-pal-pro", ["Proto-Pama-Nyungan"] = "aus-pam-pro", ["Proto-Paresi-Waura"] = "awd-prw-pro", ["Proto-Pathan"] = "ira-pat-pro", ["Proto-Pearic"] = "mkh-pea-pro", ["Proto-Permic"] = "urj-prm-pro", ["Proto-Philippine"] = "phi-pro", ["Proto-Plateau"] = "nic-plt-pro", ["Proto-Plateau Penutian"] = "nai-plp-pro", ["Proto-Pnar-Khasi-Lyngngam"] = "aav-pkl-pro", ["Proto-Polynesian"] = "poz-pol-pro", ["Proto-Pomo"] = "nai-pom-pro", ["Proto-Puroik"] = "sit-khp-pro", ["Proto-Quechuan"] = "qwe-pro", ["Proto-Rukai"] = "dru-pro", ["Proto-Ryukyuan"] = "jpx-ryu-pro", ["Proto-Sabaki"] = "bnt-sab-pro", ["Proto-Saka"] = "xsc-sak-pro", ["Proto-Saka-Wakhi"] = "xsc-skw-pro", ["Proto-Salish"] = "sal-pro", ["Proto-Sami"] = "smi-pro", ["Proto-Samoyedic"] = "syd-pro", ["Proto-Sanglechi-Ishkashimi"] = "ira-sgi-pro", ["Proto-Sara"] = "csu-sar-pro", ["Proto-Sarmatian"] = "xsc-sar-pro", ["Proto-Scythian"] = "xsc-pro", ["Proto-Selkup"] = "sel-pro", ["Proto-Semitic"] = "sem-pro", ["Proto-Shughni-Roshani"] = "ira-shr-pro", ["Proto-Shughni-Yazghulami"] = "ira-shy-pro", ["Proto-Shughni-Yazghulami-Munji"] = "ira-sym-pro", ["Proto-Sino-Tibetan"] = "sit-pro", ["Proto-Siouan"] = "sio-pro", ["Proto-Siouan-Catawban"] = "nai-sca-pro", ["Proto-Slavic"] = "sla-pro", ["Proto-Sogdic"] = "ira-sgc-pro", ["Proto-Somaloid"] = "cus-som-pro", ["Proto-Songhay"] = "son-pro", ["Proto-Sotho-Tswana"] = "bnt-sts-pro", ["Proto-South Cushitic"] = "cus-sou-pro", ["Proto-South Dravidian"] = "dra-sou-pro", ["Proto-South Dravidian I"] = "dra-sdo-pro", ["Proto-South Dravidian II"] = "dra-sdt-pro", ["Proto-South Sulawesi"] = "poz-ssw-pro", ["Proto-Southern Jê"] = "sai-sje-pro", ["Proto-Southwestern Tai"] = "tai-swe-pro", ["Proto-Ta-Arawak"] = "awd-taa-pro", ["Proto-Tai"] = "tai-pro", ["Proto-Takic"] = "azc-tak-pro", ["Proto-Taman"] = "sdv-tmn-pro", ["Proto-Tamangic"] = "sit-tam-pro", ["Proto-Tangkhulic"] = "sit-tng-pro", ["Proto-Tani"] = "sit-tan-pro", ["Proto-Taranoan"] = "sai-tar-pro", ["Proto-Tatic"] = "xme-ttc-pro", ["Proto-Tocharian"] = "ine-toc-pro", ["Proto-Totozoquean"] = "nai-tot-pro", ["Proto-Trans-New Guinea"] = "ngf-pro", ["Proto-Triqui"] = "omq-tri-pro", ["Proto-Tsezian"] = "cau-tsz-pro", ["Proto-Tsimshianic"] = "nai-tsi-pro", ["Proto-Tungusic"] = "tuw-pro", ["Proto-Tupi-Guarani"] = "tup-gua-pro", ["Proto-Tupian"] = "tup-pro", ["Proto-Turkic"] = "trk-pro", ["Proto-Ubangian"] = "nic-ubg-pro", ["Proto-Ugric"] = "urj-ugr-pro", ["Proto-Upper Cross River"] = "nic-ucr-pro", ["Proto-Uralic"] = "urj-pro", ["Proto-Utian"] = "nai-utn-pro", ["Proto-Uto-Aztecan"] = "azc-pro", ["Proto-Vietic"] = "mkh-vie-pro", ["Proto-Volta-Congo"] = "nic-vco-pro", ["Proto-Volta-Niger"] = "alv-von-pro", ["Proto-West Germanic"] = "gmw-pro", ["Proto-West Semitic"] = "sem-wes-pro", ["Proto-Western Kho-Bwa"] = "sit-khw-pro", ["Proto-Western Mande"] = "dmn-mdw-pro", ["Proto-Witotoan"] = "sai-wit-pro", ["Proto-Yeniseian"] = "qfa-yen-pro", ["Proto-Yoruba"] = "alv-yor-pro", ["Proto-Yoruboid"] = "alv-yrd-pro", ["Proto-Yukaghir"] = "qfa-yuk-pro", ["Proto-Yupik"] = "ypk-pro", ["Proto-Zapotec"] = "omq-zpc-pro", ["Proto-Zapotecan"] = "omq-zap-pro", ["Proto-Zaza-Gorani"] = "ira-zgr-pro", ["Providencia Sign Language"] = "prz", ["Psikye"] = "kvj", ["Puare"] = "pux", ["Pudtol Atta"] = "atp", ["Puebla Mazatec"] = "pbm", ["Puelche"] = "pue", ["Puerto Rican Sign Language"] = "psl", ["Puimei Naga"] = "npu", ["Puinave"] = "pui", ["Puiron"] = "sit-prn", ["Pukapukan"] = "pkp", ["Pulabu"] = "pup", ["Puluwat"] = "puw", ["Puma"] = "pum", ["Pumpokol"] = "xpm", ["Pumé"] = "yae", ["Punan Aput"] = "pud", ["Punan Bah-Biau"] = "pna", ["Punan Batu"] = "pnm", ["Punan Merah"] = "puf", ["Punan Merap"] = "puc", ["Punan Tubu"] = "puj", ["Punic"] = "xpu", ["Punjabi"] = "pa", ["Punu"] = "puu", ["Puoc"] = "puo", ["Puquina"] = "puq", ["Puragi"] = "pru", ["Purari"] = "iar", ["Purepecha"] = "pua", ["Puri"] = "prr", ["Purik"] = "prx", ["Purisimeño"] = "puy", ["Puroik"] = "suv", ["Puruborá"] = "pur", ["Puruhá"] = "sai-prh", ["Purukotó"] = "sai-pur", ["Purum"] = "pub", ["Putai"] = "mfl", ["Putoh"] = "put", ["Putukwam"] = "afe", ["Puxian Min"] = "cpx", ["Puyo-Paekche"] = "xpp", ["Puyuma"] = "pyu", ["Pwaamèi"] = "pme", ["Pwapwâ"] = "pop", ["Pyapun"] = "pcw", ["Pye Krumen"] = "pye", ["Pyemmairre"] = "xpb", ["Pyen"] = "pyy", ["Pykobjê"] = "sai-pyk", ["Pyu (Myanmar)"] = "pyx", ["Pyu (New Guinea)"] = "pby", ["Páez"] = "pbb", ["Pááfang"] = "pfa", ["Päri"] = "lkr", ["Pémono"] = "pev", ["Pévé"] = "lme", ["Pökoot"] = "pko", ["Q'anjob'al"] = "kjb", ["Q'eqchi"] = "kek", ["Qabiao"] = "laq", ["Qaqet"] = "byx", ["Qatabanian"] = "xqt", ["Qau"] = "gqu", ["Qila Muji"] = "ymq", ["Qimant"] = "ahg", ["Quapaw"] = "qua", ["Quebec Sign Language"] = "fcs", ["Quechua"] = "qu", ["Quenya"] = "qya", ["Querétaro Otomi"] = "otq", ["Quetzaltepec Mixe"] = "pxm", ["Queyu"] = "qvy", ["Quiavicuzas Zapotec"] = "zpj", ["Quileute"] = "qui", ["Quimbaya"] = "sai-qmb", ["Quinault"] = "qun", ["Quinigua"] = "nai-qng", ["Quinqui"] = "quq", ["Quioquitani-Quierí Zapotec"] = "ztq", ["Quiotepec Chinantec"] = "chq", ["Quiripi"] = "qyp", ["Quitemo"] = "sai-qtm", ["Rabha"] = "rah", ["Rabona"] = "sai-rab", ["Rade"] = "rad", ["Raetic"] = "xrr", ["Raga"] = "lml", ["Rahambuu"] = "raz", ["Rajah Kabunsuwan Manobo"] = "mqk", ["Rajbanshi"] = "rjs", ["Raji"] = "rji", ["Rajong"] = "rjg", ["Rajput Garasia"] = "gra", ["Rakahanga-Manihiki"] = "rkh", ["Rakhine"] = "rki", ["Ralte"] = "ral", ["Rama"] = "rma", ["Ramandi"] = "tks", ["Ramanos"] = "sai-ram", ["Ramoaaina"] = "rai", ["Ramopa"] = "kjx", ["Rampi"] = "lje", ["Rana Tharu"] = "thr", ["Rang"] = "rax", ["Rangkas"] = "rgk", ["Ranglong"] = "rnl", ["Rao"] = "rao", ["Rapa"] = "ray", ["Rapa Nui"] = "rap", ["Rapoisi"] = "kyx", ["Rapting"] = "rpt", ["Rara Bakati'"] = "lra", ["Rarotongan"] = "rar", ["Rasawa"] = "rac", ["Ratagnon"] = "btn", ["Ratahan"] = "rth", ["Rathawi"] = "rtw", ["Rathwi Bareli"] = "bgd", ["Raute"] = "rau", ["Ravula"] = "yea", ["Rawa"] = "rwo", ["Rawang"] = "raw", ["Rawat"] = "jnl", ["Rawo"] = "rwa", ["Rayón Zoque"] = "zor", ["Razajerdi"] = "rat", ["Razihi"] = "rzh", ["Reang"] = "ria", ["Red Gelao"] = "gir", ["Reel"] = "atu", ["Rejang"] = "rej", ["Rejang Kayan"] = "ree", ["Reli"] = "rei", ["Rema"] = "bow", ["Rembarunga"] = "rmb", ["Rembong"] = "reb", ["Remo"] = "rem", ["Remontado Agta"] = "agv", ["Rempi"] = "rmp", ["Remun"] = "lkj", ["Rendille"] = "rel", ["Rengao"] = "ren", ["Rennellese"] = "mnv", ["Repanbitip"] = "rpn", ["Rer Bare"] = "rer", ["Rerau"] = "rea", ["Rerep"] = "pgk", ["Reshe"] = "res", ["Resígaro"] = "rgr", ["Retta"] = "ret", ["Reyesano"] = "rey", ["Rhine Franconian"] = "gmw-rfr", ["Riang"] = "ril", ["Riantana"] = "ran", ["Ribun"] = "rir", ["Rigwe"] = "iri", ["Rikbaktsa"] = "rkb", ["Rinconada Bikol"] = "bto", ["Rincón Zapotec"] = "zar", ["Ringgou"] = "rgu", ["Ririo"] = "rri", ["Ritarungo"] = "rit", ["Riung"] = "riu", ["Riverain Sango"] = "snj", ["Rogo"] = "rod", ["Rohingya"] = "rhg", ["Roma"] = "rmm", ["Romagnol"] = "rgn", ["Romam"] = "rmx", ["Romani"] = "rom", ["Romanian"] = "ro", ["Romanian Sign Language"] = "rms", ["Romano-Greek"] = "rge", ["Romano-Serbian"] = "rsb", ["Romanova"] = "rmv", ["Romansh"] = "rm", ["Romblomanon"] = "rol", ["Rombo"] = "rof", ["Romkun"] = "rmk", ["Ron"] = "cla", ["Ronga"] = "rng", ["Rongga"] = "ror", ["Rongmei Naga"] = "nbu", ["Rongpo"] = "rnp", ["Ronji"] = "roe", ["Roon"] = "rnn", ["Roria"] = "rga", ["Roro"] = "rro", ["Rotokas"] = "roo", ["Rotuman"] = "rtm", ["Rouran"] = "mis-rou", ["Roviana"] = "rug", ["Ruching Palaung"] = "pce", ["Rudbari"] = "rdb", ["Rufiji"] = "rui", ["Ruga"] = "ruh", ["Rukai"] = "dru", ["Rukiga"] = "cgg", ["Ruma"] = "ruz", ["Rumai Palaung"] = "rbb", ["Rumu"] = "klq", ["Runga"] = "rou", ["Rungtu"] = "rtc", ["Rungus"] = "drg", ["Rungwa"] = "rnw", ["Russenorsk"] = "crp-rsn", ["Russian"] = "ru", ["Russian Sign Language"] = "rsl", ["Rutul"] = "rut", ["Ruuli"] = "ruc", ["Ruwund"] = "rnd", ["Rwa"] = "rwk", ["Rwanda-Rundi"] = "rw", ["Rwandan Sign Language"] = "rsn", ["Réunion Creole French"] = "rcf", ["S'gaw Karen"] = "ksw", ["Sa"] = "sax", ["Sa'a"] = "apb", ["Sa'ban"] = "snv", ["Sa'och"] = "scq", ["Saafi-Saafi"] = "sav", ["Saam"] = "raq", ["Saamia"] = "lsm", ["Saanich"] = "str", ["Saare"] = "uss", ["Saaroa"] = "sxr", ["Saba"] = "saa", ["Sabaean"] = "xsa", ["Sabah Bisaya"] = "bsy", ["Sabah Malay"] = "msi", ["Sabanê"] = "sae", ["Sabaot"] = "spy", ["Sabine"] = "sbv", ["Sabir"] = "pml", ["Sabu"] = "hvn", ["Sabüm"] = "sbo", ["Sacapulteco"] = "quv", ["Sadri"] = "sck", ["Saek"] = "skb", ["Saep"] = "spd", ["Safaitic"] = "sem-saf", ["Safaliba"] = "saf", ["Safeyoka"] = "apz", ["Safwa"] = "sbk", ["Sagala"] = "sbm", ["Sagalla"] = "tga", ["Sahaptin"] = "nai-spt", ["Saho"] = "ssy", ["Sahu"] = "saj", ["Saisiyat"] = "xsy", ["Sajau Basap"] = "sjb", ["Sakachep"] = "sch", ["Sakam"] = "skm", ["Sakao"] = "sku", ["Sakata"] = "skt", ["Sake"] = "sak", ["Sakizaya"] = "szy", ["Sala"] = "shq", ["Salampasu"] = "slx", ["Salar"] = "slr", ["Salas"] = "sgu", ["Saleman"] = "sau", ["Saliba (Colombia)"] = "slc", ["Saliba (New Guinea)"] = "sbe", ["Salinan"] = "sln", ["Salt-Yui"] = "sll", ["Saluan"] = "loe", ["Salumá"] = "slj", ["Salvadoran Lenca"] = "nai-sln", ["Salvadoran Sign Language"] = "esn", ["Sam"] = "snx", ["Sama"] = "smd", ["Samalian"] = "sem-sam", ["Samaritan Aramaic"] = "sam", ["Samaritan Hebrew"] = "smp", ["Samarokena"] = "tmj", ["Samatao"] = "ysd", ["Samba"] = "smx", ["Sambali"] = "xsb", ["Sambalpuri"] = "spv", ["Sambe"] = "xab", ["Samberigi"] = "ssx", ["Samburu"] = "saq", ["Samei"] = "smh", ["Samo"] = "smq", ["Samoan"] = "sm", ["Samoan Plantation Pidgin"] = "crp-spp", ["Samogitian"] = "sgs", ["Samosa"] = "swm", ["Sampang"] = "rav", ["Samre"] = "sxm", ["Samtao"] = "stu", ["Samvedi"] = "smv", ["San Agustín Mixtepec Zapotec"] = "ztm", ["San Andres Creole English"] = "icr", ["San Baltazar Loxicha Zapotec"] = "zpx", ["San Felipe Otlaltepec Popoloca"] = "pow", ["San Jerónimo Tecóatl Mazatec"] = "maa", ["San Juan Atzingo Popoloca"] = "poe", ["San Juan Colorado Mixtec"] = "mjc", ["San Juan Guelavía Zapotec"] = "zab", ["San Juan Quiahije Chatino"] = "omq-sjq", ["San Juan Teita Mixtec"] = "xtj", ["San Luís Temalacayuca Popoloca"] = "pps", ["San Marcos Tlalcoyalco Popoloca"] = "pls", ["San Martín Itunyoso Triqui"] = "trq", ["San Miguel Creole French"] = "scf", ["San Miguel Piedras Mixtec"] = "xtp", ["San Miguel el Grande Mixtec"] = "mig", ["San Pablo Güilá Zapotec"] = "ztu", ["San Pedro Amuzgos Amuzgo"] = "azg", ["San Pedro Quiatoni Zapotec"] = "zpf", ["San Vicente Coatlán Zapotec"] = "zpt", ["Sanapaná"] = "spn", ["Sanaviron"] = "sai-san", ["Sandawe"] = "sad", ["Sanga (Congo)"] = "sng", ["Sanga (Nigeria)"] = "xsn", ["Sanggau"] = "scg", ["Sangil"] = "snl", ["Sangir"] = "sxn", ["Sangisari"] = "sgr", ["Sangkong"] = "sgk", ["Sanglechi"] = "sgy", ["Sango"] = "sg", ["Sangtam Naga"] = "nsa", ["Sangu (Gabon)"] = "snq", ["Sangu (Tanzania)"] = "sbp", ["Sani"] = "ysn", ["Sanie"] = "ysy", ["Saniyo-Hiyewe"] = "sny", ["Sankaran Maninka"] = "msc", ["Sansi"] = "ssi", ["Sanskrit"] = "sa", ["Santa Catarina Albarradas Zapotec"] = "ztn", ["Santa Inés Ahuatempan Popoloca"] = "pca", ["Santa Inés Yatzechi Zapotec"] = "zpn", ["Santa Lucía Monteverde Mixtec"] = "mdv", ["Santa María La Alta Nahuatl"] = "nhz", ["Santa María Quiegolani Zapotec"] = "zpi", ["Santa María Zacatepec Mixtec"] = "mza", ["Santa Teresa Cora"] = "cok", ["Santali"] = "sat", ["Santiago Xanica Zapotec"] = "zpr", ["Santo Domingo Albarradas Zapotec"] = "zas", ["Sanumá"] = "xsu", ["Sanxiang Min"] = "nan-zsh", ["Sapa"] = "tys", ["Saparua"] = "spr", ["Sapará"] = "sai-sap", ["Sapo"] = "krn", ["Saponi"] = "spi", ["Saposa"] = "sps", ["Sapuan"] = "spu", ["Sapé"] = "spc", ["Sar"] = "mwm", ["Sara"] = "sre", ["Sara Kaba"] = "sbz", ["Sara Kaba Deme"] = "kwg", ["Sara Kaba Náà"] = "kwv", ["Saraiki"] = "skr", ["Saramaccan"] = "srm", ["Sarangani Blaan"] = "bps", ["Sarangani Manobo"] = "mbs", ["Sarasira"] = "zsa", ["Saraveca"] = "sar", ["Sarawak Malay"] = "poz-sml", ["Sarazi"] = "inc-sar", ["Sardinian"] = "sc", ["Sarikoli"] = "srh", ["Sarli"] = "sdf", ["Sartang"] = "onp", ["Sarua"] = "swy", ["Sarudu"] = "sdu", ["Saruga"] = "sra", ["Sasak"] = "sas", ["Sasaru"] = "sxs", ["Sassarese"] = "sdc", ["Satawalese"] = "stw", ["Saterland Frisian"] = "stq", ["Sateré-Mawé"] = "mav", ["Sathmar Swabian"] = "gmw-stm", ["Saudi Arabian Sign Language"] = "sdl", ["Saurashtra"] = "saz", ["Sauri"] = "srt", ["Sause"] = "sao", ["Sausi"] = "ssj", ["Savi"] = "sdg", ["Savosavo"] = "svs", ["Sawai"] = "szw", ["Saweru"] = "swr", ["Sawi"] = "saw", ["Sawila"] = "swt", ["Sawriya Paharia"] = "mjt", ["Saxwe Gbe"] = "sxw", ["Saya"] = "say", ["Sayula Popoluca"] = "pos", ["Scanian"] = "gmq-scy", ["Scots"] = "sco", ["Scottish Gaelic"] = "gd", ["Seba"] = "kdg", ["Sebat Bet Gurage"] = "sgw", ["Seberuang"] = "sbx", ["Sebop"] = "sib", ["Sebuyau"] = "snb", ["Sechelt"] = "sec", ["Sechura"] = "sai-sec", ["Secoya"] = "sey", ["Sedang"] = "sed", ["Sedoa"] = "tvw", ["Seenku"] = "sos", ["Segai"] = "sge", ["Segeju"] = "seg", ["Seget"] = "sbg", ["Sehwi"] = "sfw", ["Seimat"] = "ssg", ["Seit-Kaitetu"] = "hik", ["Sekani"] = "sek", ["Sekapan"] = "skp", ["Sekar"] = "skz", ["Seke"] = "skj", ["Sekele"] = "vaj", ["Seki"] = "syi", ["Seko Padang"] = "skx", ["Seko Tengah"] = "sko", ["Sekpele"] = "lip", ["Selangor Sign Language"] = "kgi", ["Selaru"] = "slu", ["Selayar"] = "sly", ["Selee"] = "snw", ["Selepet"] = "spl", ["Selk'nam"] = "ona", ["Selonian"] = "sxl", ["Selungai Murut"] = "slg", ["Seluwasan"] = "sws", ["Sema"] = "nsm", ["Semai"] = "sea", ["Semandang"] = "sdm", ["Semaq Beri"] = "szc", ["Sembakung Murut"] = "sbr", ["Semelai"] = "sza", ["Semigallian"] = "xzm", ["Semimi"] = "etz", ["Semnam"] = "ssm", ["Semnani"] = "smy", ["Sempan"] = "xse", ["Sena"] = "seh", ["Senara"] = "seq", ["Senaya"] = "syn", ["Sene"] = "sej", ["Seneca"] = "see", ["Sengele"] = "szg", ["Senggi"] = "snu", ["Sengo"] = "spk", ["Sengseng"] = "ssz", ["Senhaja de Srair"] = "sjs", ["Sensi"] = "sni", ["Sentani"] = "set", ["Senthang Chin"] = "sez", ["Sentinelese"] = "std", ["Sepa (Indonesia)"] = "spb", ["Sepa (New Guinea)"] = "spe", ["Sepen"] = "spm", ["Sepik Iwam"] = "iws", ["Sera"] = "sry", ["Serbo-Croatian"] = "sh", ["Sere"] = "swf", ["Serer"] = "srr", ["Seri"] = "sei", ["Serili"] = "sve", ["Seroa"] = "kqu", ["Serrano"] = "ser", ["Seru"] = "szd", ["Serua"] = "srw", ["Serudung Murut"] = "srk", ["Serui-Laut"] = "seu", ["Seta"] = "stf", ["Setaman"] = "stm", ["Seti"] = "sbi", ["Severn Ojibwa"] = "ojs", ["Sewa Bay"] = "sew", ["Seychelles Sign Language"] = "lsw", ["Seychellois Creole"] = "crs", ["Seze"] = "sze", ["Sha"] = "scw", ["Shabak"] = "sdb", ["Shabo"] = "sbf", ["Shahmirzadi"] = "srz", ["Shahrudi"] = "shm", ["Shall-Zwall"] = "sha", ["Shama-Sambuga"] = "sqa", ["Shamang"] = "xsh", ["Shambala"] = "ksb", ["Shan"] = "shn", ["Shanenawa"] = "swo", ["Shanga"] = "sho", ["Shangzhai"] = "jih", ["Shaojiang Min"] = "sjc", ["Shaozhou Tuhua"] = "zhx-sht", ["Sharanahua"] = "mcd", ["Shark Bay"] = "ssv", ["Sharwa"] = "swq", ["Shasta"] = "sht", ["Shatt"] = "shj", ["Shau"] = "sqh", ["Shawnee"] = "sjw", ["She"] = "shx", ["Shebayo"] = "awd-she", ["Shehri"] = "shv", ["Shekkacho"] = "moy", ["Sheko"] = "she", ["Shelta"] = "sth", ["Sheni"] = "scv", ["Sherbro"] = "bun", ["Sherdukpen"] = "sdp", ["Sherpa"] = "xsr", ["Sheshi Kham"] = "kip", ["Shetland"] = "scz", ["Shi"] = "shr", ["Shihhi Arabic"] = "ssh", ["Shiki"] = "gua", ["Shilluk"] = "shk", ["Shina"] = "scl", ["Shinasha"] = "bwo", ["Shipibo-Conibo"] = "shp", ["Shixing"] = "sxg", ["Sholaga"] = "sle", ["Shom Peng"] = "sii", ["Shona"] = "sn", ["Shoo-Minda-Nye"] = "bcv", ["Shor"] = "cjs", ["Shoshone"] = "shh", ["Shua"] = "shg", ["Shuar"] = "jiv", ["Shughni"] = "sgh", ["Shumashti"] = "sts", ["Shumcho"] = "scu", ["Shuswap"] = "shs", ["Shuwa-Zamani"] = "ksa", ["Shwai"] = "shw", ["Shwe Palaung"] = "pll", ["Sialum"] = "slw", ["Siamou"] = "sif", ["Sian"] = "spg", ["Siane"] = "snp", ["Siang"] = "sya", ["Siar-Lak"] = "sjr", ["Sibe"] = "nco", ["Siberian Tatar"] = "sty", ["Sibu Melanau"] = "sdx", ["Sicanian"] = "sxc", ["Sicel"] = "scx", ["Sichuanese"] = "zhx-sic", ["Sicilian"] = "scn", ["Sicite"] = "sep", ["Siculo-Arabic"] = "sqr", ["Sidamo"] = "sid", ["Sidetic"] = "xsd", ["Sie"] = "erg", ["Sierra Leone Sign Language"] = "sgx", ["Sierra Negra Nahuatl"] = "nsu", ["Sierra de Juárez Zapotec"] = "zaa", ["Sighu"] = "sxe", ["Sihan"] = "snr", ["Sika"] = "ski", ["Sikaiana"] = "sky", ["Sikaritai"] = "tty", ["Sikiana"] = "sik", ["Sikkimese"] = "sip", ["Sikule"] = "skh", ["Sila"] = "slt", ["Silacayoapan Mixtec"] = "mks", ["Silesian"] = "szl", ["Silimo"] = "wul", ["Siliput"] = "mkc", ["Silopi"] = "xsp", ["Silt'e"] = "stv", ["Simaa"] = "sie", ["Simalungun Batak"] = "bts", ["Simba"] = "sbw", ["Simbali"] = "smg", ["Simbari"] = "smb", ["Simbo"] = "sbb", ["Simeku"] = "smz", ["Simeulue"] = "smr", ["Simte"] = "smt", ["Sinacantán"] = "nai-sin", ["Sinagen"] = "siu", ["Sinasina"] = "sst", ["Sinaugoro"] = "snc", ["Sindarin"] = "sjn", ["Sindhi"] = "sd", ["Sindhi Bhil"] = "sbn", ["Sindihui Mixtec"] = "xts", ["Singa"] = "sgm", ["Singapore Sign Language"] = "sls", ["Singpho"] = "sgp", ["Sinhalese"] = "si", ["Sinicahua Mixtec"] = "xti", ["Sininkere"] = "skq", ["Sinte Romani"] = "rmo", ["Sinyar"] = "sys", ["Sinúfana"] = "sai-sin", ["Sio"] = "xsi", ["Siona"] = "snn", ["Sipakapense"] = "qum", ["Sira"] = "swj", ["Siraya"] = "fos", ["Sirenik"] = "ysr", ["Siri"] = "sir", ["Siriano"] = "sri", ["Sirionó"] = "srq", ["Sirmauri"] = "srx", ["Siroi"] = "ssd", ["Sirva"] = "sbq", ["Sisaala"] = "sld", ["Sissano"] = "sso", ["Situ"] = "sit-sit", ["Siuslaw"] = "sis", ["Sivandi"] = "siy", ["Sivia Sign Language"] = "lsv", ["Siwai"] = "siw", ["Siwi"] = "siz", ["Siwu"] = "akp", ["Siyin Chin"] = "csy", ["Skalvian"] = "svx", ["Ske"] = "ske", ["Skepi Creole Dutch"] = "skw", ["Skolt Sami"] = "sms", ["Skou"] = "skv", ["Slavomolisano"] = "svm", ["Slovak"] = "sk", ["Slovakian Sign Language"] = "svk", ["Slovene"] = "sl", ["Slovincian"] = "zlw-slv", ["Small Flowery Miao"] = "sfm", ["Smärky Kanum"] = "kxq", ["So'a"] = "ssq", ["Sobei"] = "sob", ["Sochiapam Chinantec"] = "cso", ["Soga"] = "xog", ["Sogdian"] = "sog", ["Sok"] = "skk", ["Sokna"] = "swn", ["Soko"] = "soc", ["Sokoro"] = "sok", ["Solano"] = "xso", ["Soli"] = "sby", ["Solombala English"] = "crp-slb", ["Solomon Islands Sign Language"] = "szs", ["Solon"] = "tuw-sol", ["Solong"] = "aaw", ["Solos"] = "sol", ["Som"] = "smc", ["Somali"] = "so", ["Somba-Siawari"] = "bmu", ["Somra"] = "ntx", ["Somrai"] = "sor", ["Somray"] = "smu", ["Somyev"] = "kgt", ["Sonaga"] = "ysg", ["Sonde"] = "shc", ["Songe"] = "sop", ["Songlai Chin"] = "csj", ["Songomeno"] = "soe", ["Songoora"] = "sod", ["Sonha"] = "soi", ["Sonia"] = "siq", ["Soninke"] = "snk", ["Sonsorolese"] = "sov", ["Soo"] = "teu", ["Sop"] = "urw", ["Soqotri"] = "sqt", ["Sora"] = "srb", ["Sori-Harengan"] = "sbh", ["Sorkhei"] = "sqo", ["Sorothaptic"] = "sxo", ["Sorsogon Ayta"] = "ays", ["Sos Kundi"] = "sdk", ["Sota Kanum"] = "krz", ["Sotho"] = "st", ["Sou"] = "sqq", ["Sougb"] = "mnx", ["South African Sign Language"] = "sfs", ["South Awyu"] = "aws", ["South Boma"] = "bnt-sbo", ["South Central Banda"] = "lnl", ["South Central Dinka"] = "dib", ["South Efate"] = "erk", ["South Fali"] = "fal", ["South Giziga"] = "giz", ["South Lembata"] = "lmf", ["South Levantine Arabic"] = "ajp", ["South Marquesan"] = "mqm", ["South Muyu"] = "kts", ["South Nuaulu"] = "nxl", ["South Picene"] = "spx", ["South Slavey"] = "xsl", ["South Tairora"] = "omw", ["South Tukang Besi"] = "bhq", ["South Ucayali Ashéninka"] = "cpy", ["South Watut"] = "mcy", ["Southeast Ambrym"] = "tvk", ["Southeast Babar"] = "vbb", ["Southeast Ijo"] = "ijs", ["Southeast Pashayi"] = "psi", ["Southeast Tasmanian"] = "xpf", ["Southeastern Dinka"] = "dks", ["Southeastern Ixtlán Zapotec"] = "zpd", ["Southeastern Kolami"] = "nit", ["Southeastern Nochixtlán Mixtec"] = "mxy", ["Southeastern Pomo"] = "pom", ["Southeastern Puebla Nahuatl"] = "npl", ["Southeastern Tarahumara"] = "tcu", ["Southeastern Tepehuan"] = "stp", ["Southern Alta"] = "agy", ["Southern Altai"] = "alt", ["Southern Amami Ōshima"] = "ams", ["Southern Bai"] = "bfs", ["Southern Birifor"] = "biv", ["Southern Bobo"] = "bwq", ["Southern Bontoc"] = "obk", ["Southern Carrier"] = "caf", ["Southern Catanduanes Bikol"] = "bln", ["Southern Dagaare"] = "dga", ["Southern East Cree"] = "crj", ["Southern Ghale"] = "ghe", ["Southern Grebo"] = "grj", ["Southern Guiyang Hmong"] = "hmy", ["Southern Haida"] = "hax", ["Southern Hindko"] = "hnd", ["Southern Kalapuya"] = "sxk", ["Southern Kalinga"] = "ksc", ["Southern Kam"] = "kmc", ["Southern Khanty"] = "kca-sou", ["Southern Kissi"] = "kss", ["Southern Kiwai"] = "kjd", ["Southern Kurdish"] = "sdh", ["Southern Lolopo"] = "ysp", ["Southern Lorung"] = "lrr", ["Southern Luri"] = "luz", ["Southern Ma'di"] = "snm", ["Southern Mansi"] = "mns-sou", ["Southern Mashan Hmong"] = "hma", ["Southern Mnong"] = "mnn", ["Southern Muji"] = "ymc", ["Southern Ndebele"] = "nr", ["Southern Ngbandi"] = "nbw", ["Southern Nicobarese"] = "nik", ["Southern Nisu"] = "nsd", ["Southern Nuni"] = "nnw", ["Southern Ohlone"] = "css", ["Southern One"] = "osu", ["Southern Pame"] = "pmz", ["Southern Pinghua"] = "csp", ["Southern Pomo"] = "peq", ["Southern Puebla Mixtec"] = "mit", ["Southern Pumi"] = "pmj", ["Southern Qiandong Miao"] = "hms", ["Southern Qiang"] = "qxs", ["Southern Rengma Naga"] = "nre", ["Southern Rincon Zapotec"] = "zsr", ["Southern Roglai"] = "rgs", ["Southern Sama"] = "ssb", ["Southern Sami"] = "sma", ["Southern Samo"] = "sbd", ["Southern Selkup"] = "sel-sou", ["Southern Sierra Miwok"] = "skd", ["Southern Thai"] = "sou", ["Southern Tidung"] = "itd", ["Southern Tiwa"] = "tix", ["Southern Toussian"] = "wib", ["Southern Tujia"] = "tjs", ["Southern Tutchone"] = "tce", ["Southern Valley Yokuts"] = "yok-svy", ["Southern Yukaghir"] = "yux", ["Southwest Gbaya"] = "gso", ["Southwest Palawano"] = "plv", ["Southwest Pashayi"] = "psh", ["Southwest Tanna"] = "nwi", ["Southwestern Bontoc"] = "vbk", ["Southwestern Dinka"] = "dik", ["Southwestern Fars"] = "fay", ["Southwestern Guiyang Hmong"] = "hmg", ["Southwestern Huishui Hmong"] = "hmh", ["Southwestern Tarahumara"] = "twr", ["Southwestern Tepehuan"] = "tla", ["Southwestern Tlaxiaco Mixtec"] = "meh", ["Sowa"] = "sww", ["Sowanda"] = "sow", ["Soyaltepec Mazatec"] = "vmp", ["Soyaltepec Mixtec"] = "vmq", ["Spanish"] = "es", ["Spanish Sign Language"] = "ssp", ["Spiti Bhoti"] = "spt", ["Spokane"] = "spo", ["Squamish"] = "squ", ["Sranan Tongo"] = "srn", ["Sri Lankan Creole Malay"] = "sci", ["Sri Lankan Sign Language"] = "sqs", ["Stau"] = "ero-tau", ["Stod Bhoti"] = "sbu", ["Stoney"] = "sto", ["Suabo"] = "szp", ["Suau"] = "swp", ["Suba"] = "sxb", ["Suba-Simbiti"] = "ssc", ["Subi"] = "xsj", ["Subiya"] = "sbs", ["Subtiaba"] = "sut", ["Sudanese Arabic"] = "apd", ["Sudest"] = "tgo", ["Sudovian"] = "xsv", ["Suena"] = "sue", ["Suga"] = "sgi", ["Suganga"] = "sug", ["Sugut Dusun"] = "kzs", ["Sui"] = "swi", ["Suki"] = "sui", ["Suku"] = "sub", ["Sukuma"] = "suk", ["Sukur"] = "syk", ["Sukurum"] = "zsu", ["Sula"] = "szn", ["Sulka"] = "sua", ["Sulod"] = "srg", ["Suma"] = "sqm", ["Sumariup"] = "siv", ["Sumau"] = "six", ["Sumbawa"] = "smw", ["Sumbwa"] = "suw", ["Sumerian"] = "sux", ["Sumtu Chin"] = "csv", ["Sunam"] = "ssk", ["Sundanese"] = "su", ["Sungwadaga"] = "mwo", ["Sungwadia"] = "mrb", ["Sunum"] = "ymn", ["Sunwar"] = "suz", ["Suoy"] = "syo", ["Supyire"] = "spp", ["Sur"] = "tdl", ["Surbakhal"] = "sbj", ["Suri"] = "suq", ["Surigaonon"] = "sgd", ["Surjapuri"] = "sjp", ["Sursurunga"] = "sgz", ["Suruahá"] = "swx", ["Surubu"] = "sde", ["Suruí"] = "sru", ["Suruí Do Pará"] = "mdz", ["Susquehannock"] = "sqn", ["Susu"] = "sus", ["Susuami"] = "ssu", ["Suundi"] = "sdj", ["Suwawa"] = "swu", ["Suyá"] = "suy", ["Svan"] = "sva", ["Swabian"] = "swg", ["Swahili"] = "sw", ["Swampy Cree"] = "csw", ["Swazi"] = "ss", ["Swedish"] = "sv", ["Swedish Sign Language"] = "swl", ["Swiss-French Sign Language"] = "ssr", ["Swiss-German Sign Language"] = "sgg", ["Swiss-Italian Sign Language"] = "slf", ["Swo"] = "sox", ["Syenara"] = "shz", ["Sylheti"] = "syl", ["Sácata"] = "sai-sac", ["São Paulo Kaingáng"] = "zkp", ["Sãotomense"] = "cri", ["Sîshëë"] = "sih", ["Sô"] = "sss", ["T'en"] = "tct", ["Taabwa"] = "tap", ["Taba"] = "mky", ["Tabaa Zapotec"] = "zat", ["Tabancale"] = "sai-tab", ["Tabaru"] = "tby", ["Tabasaran"] = "tab", ["Tabasco Nahuatl"] = "nhc", ["Tabasco Zoque"] = "zoq", ["Tabla"] = "tnm", ["Tabo"] = "knv", ["Tabriak"] = "tzx", ["Tacahua Mixtec"] = "xtt", ["Tacana"] = "tna", ["Tachawit"] = "shy", ["Tadaksahak"] = "dsq", ["Tadyawan"] = "tdy", ["Tae'"] = "rob", ["Tafi"] = "tcd", ["Tafreshi"] = "xme-taf", ["Tagabawa"] = "bgs", ["Tagakaulu Kalagan"] = "klg", ["Tagal Murut"] = "mvv", ["Tagalog"] = "tl", ["Tagbu"] = "tbm", ["Tagdal"] = "tda", ["Tagish"] = "tgx", ["Tagoi"] = "tag", ["Tagwana"] = "tgw", ["Tahitian"] = "ty", ["Tahltan"] = "tht", ["Tai"] = "taw", ["Tai Daeng"] = "tyr", ["Tai Dam"] = "blt", ["Tai Do"] = "tyj", ["Tai Dón"] = "twh", ["Tai Hang Tong"] = "thc", ["Tai Hongjin"] = "tiz", ["Tai Laing"] = "tjl", ["Tai Loi"] = "tlq", ["Tai Long"] = "thi", ["Tai Nüa"] = "tdd", ["Tai Pao"] = "tpo", ["Tai Thanh"] = "tmm", ["Tai Ya"] = "cuu", ["Taiap"] = "gpn", ["Taikat"] = "aos", ["Taimyr Pidgin Russian"] = "crp-tpr", ["Tainae"] = "ago", ["Tairuma"] = "uar", ["Taishanese"] = "zhx-tai", ["Taita"] = "dav", ["Taivoan"] = "tvx", ["Taiwan Sign Language"] = "tss", ["Taje"] = "pee", ["Tajik"] = "tg", ["Tajiki Arabic"] = "abh", ["Tajio"] = "tdj", ["Tajuasohn"] = "tja", ["Takelma"] = "tkm", ["Takia"] = "tbc", ["Takua"] = "tkz", ["Takuu"] = "nho", ["Takwane"] = "tke", ["Tal"] = "tal", ["Tala"] = "tak", ["Talaud"] = "tld", ["Taliabu"] = "tlv", ["Talieng"] = "tdf", ["Talinga-Bwisi"] = "tlj", ["Talise"] = "tlr", ["Tallán"] = "sai-tal", ["Talodi"] = "tlo", ["Taloki"] = "tlk", ["Talondo'"] = "tln", ["Talossan"] = "tzl", ["Talu"] = "yta", ["Talysh"] = "tly", ["Tama (Chad)"] = "tma", ["Tama (Colombia)"] = "ten", ["Tamagario"] = "tcg", ["Tamambo"] = "mla", ["Taman (Indonesia)"] = "tmn", ["Taman (Myanmar)"] = "tcl", ["Tamanaku"] = "tmz", ["Tamazola Mixtec"] = "vmx", ["Tambas"] = "tdk", ["Tambora"] = "xxt", ["Tambotalo"] = "tls", ["Tami"] = "tmy", ["Tamil"] = "ta", ["Tamki"] = "tax", ["Tamnim Citak"] = "tml", ["Tampias Lobu"] = "low", ["Tampuan"] = "tpu", ["Tampulma"] = "tpm", ["Tanacross"] = "tcb", ["Tanah Merah"] = "tcm", ["Tanapag"] = "tpv", ["Tanchangya"] = "tnv", ["Tandaganon"] = "tgn", ["Tandia"] = "tni", ["Tanema"] = "tnx", ["Tangale"] = "tan", ["Tangam"] = "sit-tgm", ["Tanggu"] = "tgu", ["Tangkhul Naga"] = "nmf", ["Tangko"] = "tkx", ["Tangoa"] = "tgp", ["Tangsa"] = "nst", ["Tanguat"] = "tbs", ["Tangut"] = "txg", ["Tangwang"] = "mis-tnw", ["Tanimbili"] = "tbe", ["Tanimuca-Retuarã"] = "tnc", ["Tanjijili"] = "uji", ["Tanudan Kalinga"] = "kml", ["Tanzanian Sign Language"] = "tza", ["Taos"] = "twf", ["Tapachultec"] = "nai-tap", ["Taparita"] = "sai-tpr", ["Tapayuna"] = "sai-tap", ["Tapei"] = "afp", ["Tapieté"] = "tpj", ["Tapirapé"] = "taf", ["Tar Gula"] = "kcm", ["Tara Baka"] = "bdh", ["Tarairiú"] = "sai-trr", ["Tarantino"] = "roa-tar", ["Tarao"] = "tro", ["Taraon"] = "mhu", ["Tareng"] = "tgr", ["Tariana"] = "tae", ["Tarifit"] = "rif", ["Tarjumo"] = "txj", ["Tarok"] = "yer", ["Taroko"] = "trv", ["Tarpia"] = "tpf", ["Tartessian"] = "txr", ["Taruma"] = "tdm", ["Tasawaq"] = "twq", ["Tashelhit"] = "shi", ["Tasmate"] = "tmt", ["Tat"] = "ttt", ["Tataltepec Chatino"] = "cta", ["Tatana"] = "txx", ["Tatar"] = "tt", ["Tataviam"] = "azc-tat", ["Tatuyo"] = "tav", ["Tauade"] = "ttd", ["Taulil"] = "tuh", ["Taungyo"] = "tco", ["Taupota"] = "tpa", ["Tause"] = "tad", ["Taushiro"] = "trr", ["Tausug"] = "tsg", ["Tauya"] = "tya", ["Taveta"] = "tvs", ["Tavoyan"] = "tvn", ["Tavringer Romani"] = "rmu", ["Tawala"] = "tbo", ["Tawandê"] = "xtw", ["Tawang Monpa"] = "twm", ["Tawasa"] = "nai-taw", ["Taworta"] = "tbp", ["Tawoyan"] = "twy", ["Tawr Chin"] = "tcp", ["Tay Khang"] = "tnu", ["Taymanitic"] = "sem-tay", ["Tayo"] = "cks", ["Taíno"] = "tnq", ["Tboli"] = "tbl", ["Tchitchege"] = "tck", ["Tchumbuli"] = "bqa", ["Te'un"] = "tve", ["Teanu"] = "tkw", ["Tebul Sign Language"] = "tsy", ["Tebul Ure Dogon"] = "dtu", ["Tecpatlán Totonac"] = "tcw", ["Tedaga"] = "tuq", ["Tedim Chin"] = "ctd", ["Tee"] = "tkq", ["Tefaro"] = "tfo", ["Tegali"] = "ras", ["Tehit"] = "kps", ["Tehuelche"] = "teh", ["Teiwa"] = "twe", ["Tejalapan Zapotec"] = "ztt", ["Teke-Fuumu"] = "ifm", ["Teke-Kukuya"] = "kkw", ["Teke-Laali"] = "lli", ["Teke-Tege"] = "teg", ["Teke-Tsaayi"] = "tyi", ["Teke-Tyee"] = "tyx", ["Tektiteko"] = "ttc", ["Tela-Masbuar"] = "tvm", ["Telefol"] = "tlf", ["Telugu"] = "te", ["Teluti"] = "tlt", ["Tem"] = "kdh", ["Temascaltepec Nahuatl"] = "nhv", ["Tembé"] = "tqb", ["Teme"] = "tdo", ["Temein"] = "teq", ["Temi"] = "soz", ["Temiar"] = "tea", ["Temne"] = "tem", ["Temoaya Otomi"] = "ott", ["Temoq"] = "tmo", ["Ten'edn"] = "tnz", ["Tenango Otomi"] = "otn", ["Tene Kan Dogon"] = "dtk", ["Tenggarong Kutai Malay"] = "vkt", ["Tengger"] = "tes", ["Tenharim"] = "pah", ["Tenino"] = "tqn", ["Tenis"] = "tns", ["Tennet"] = "tex", ["Teochew"] = "nan-tws", ["Teojomulco Chatino"] = "omq-teo", ["Teop"] = "tio", ["Teor"] = "tev", ["Tepecano"] = "tep", ["Tepetotutla Chinantec"] = "cnt", ["Tepeuxila Cuicatec"] = "cux", ["Tepinapa Chinantec"] = "cte", ["Tepo Krumen"] = "ted", ["Teposcolula Mixtec"] = "omq-tel", ["Tequistlatec"] = "nai-teq", ["Ter Sami"] = "sjt", ["Tera"] = "ttr", ["Terebu"] = "trb", ["Terei"] = "buo", ["Terengganu Malay"] = "poz-ter", ["Tereno"] = "ter", ["Teressa"] = "tef", ["Tereweng"] = "twg", ["Teribe"] = "tfr", ["Terik"] = "tec", ["Termanu"] = "twu", ["Ternate"] = "tft", ["Ternateño"] = "tmg", ["Tese"] = "keg", ["Teshenawa"] = "twc", ["Tetela"] = "tll", ["Tetelcingo Nahuatl"] = "nhg", ["Tetete"] = "teb", ["Tetserret"] = "tez", ["Tetum"] = "tet", ["Tetun Dili"] = "tdt", ["Teushen"] = "sai-teu", ["Teutila Cuicatec"] = "cut", ["Tewa"] = "tew", ["Texcatepec Otomi"] = "otx", ["Texistepec Popoluca"] = "poq", ["Texmelucan Zapotec"] = "zpz", ["Tezoatlán Mixtec"] = "mxb", ["Tha"] = "thy", ["Thachanadan"] = "thn", ["Thado Chin"] = "tcz", ["Thai"] = "th", ["Thai Mon"] = "mnw-tha", ["Thai Sign Language"] = "tsq", ["Thai Song"] = "soa", ["Thaiphum Chin"] = "cth", ["Thakali"] = "ths", ["Thamudic"] = "sem-tha", ["Thangal Naga"] = "nki", ["Thangmi"] = "thf", ["Thao"] = "ssf", ["Tharaka"] = "thk", ["Tharrgari"] = "dhr", ["Thavung"] = "thm", ["Thawa"] = "xtv", ["Tho"] = "tou", ["Thompson"] = "thp", ["Thopho"] = "ytp", ["Thracian"] = "txh", ["Thu Lao"] = "tyl", ["Thulung"] = "tdh", ["Thurawal"] = "tbh", ["Thuri"] = "thu", ["Tiagba"] = "ahi", ["Tiale"] = "mnl", ["Tiang"] = "tbj", ["Tibea"] = "ngy", ["Tibetan"] = "bo", ["Tibetan Sign Language"] = "lsn", ["Ticuna"] = "tca", ["Tidaá Mixtec"] = "mtx", ["Tidore"] = "tvo", ["Tiemacèwè Bozo"] = "boo", ["Tiene"] = "tii", ["Tifal"] = "tif", ["Tigak"] = "tgc", ["Tigon Mbembe"] = "nza", ["Tigre"] = "tig", ["Tigrinya"] = "ti", ["Tii"] = "txq", ["Tijaltepec Mixtec"] = "xtl", ["Tikar"] = "tik", ["Tikopia"] = "tkp", ["Tilapa Otomi"] = "otl", ["Tillamook"] = "til", ["Tilquiapan Zapotec"] = "zts", ["Tilung"] = "tij", ["Tima"] = "tms", ["Timbe"] = "tim", ["Timor Pidgin"] = "tvy", ["Timote"] = "sai-tim", ["Timucua"] = "tjm", ["Timugon Murut"] = "tih", ["Tinani"] = "lbf", ["Tindi"] = "tin", ["Tingui-Boto"] = "tgv", ["Tinigua"] = "tit", ["Tinoc Kallahan"] = "tne", ["Tinputz"] = "tpz", ["Tinrin"] = "cir", ["Tipai"] = "nai-tip", ["Tippera"] = "tpe", ["Tira"] = "tic", ["Tirahi"] = "tra", ["Tiranige Diga Dogon"] = "tde", ["Tirax"] = "mme", ["Tiruray"] = "tiy", ["Tita"] = "tdq", ["Titan"] = "ttv", ["Tiv"] = "tiv", ["Tiwa"] = "lax", ["Tiwi"] = "tiw", ["Tiéfo"] = "tiq", ["Tiéyaxo Bozo"] = "boz", ["Tjurruru"] = "tju", ["Tlachichilco Tepehua"] = "tpt", ["Tlacoapa Me'phaa"] = "tpl", ["Tlacoatzintepec Chinantec"] = "ctl", ["Tlacolulita Zapotec"] = "zpk", ["Tlahuica"] = "ocu", ["Tlahuitoltepec Mixe"] = "mxp", ["Tlamacazapa Nahuatl"] = "nuz", ["Tlazoyaltepec Mixtec"] = "mqh", ["Tlingit"] = "tli", ["To"] = "toz", ["To'abaita"] = "mlu", ["Toaripi"] = "tqo", ["Toba"] = "tob", ["Toba Batak"] = "bbc", ["Toba-Maskoy"] = "tmf", ["Tobagonian Creole English"] = "tgh", ["Tobanga"] = "tng", ["Tobati"] = "tti", ["Tobelo"] = "tlb", ["Tobian"] = "tox", ["Tobilung"] = "tgb", ["Tobo"] = "tbv", ["Tocantins Asurini"] = "asu", ["Tocharian A"] = "xto", ["Tocharian B"] = "txb", ["Tocho"] = "taz", ["Toda"] = "tcx", ["Todrah"] = "tdr", ["Tofa"] = "kim", ["Tofanma"] = "tlg", ["Tofin Gbe"] = "tfi", ["Togbo-Vara Banda"] = "tor", ["Togoyo"] = "tgy", ["Tojolabal"] = "toj", ["Tok Pisin"] = "tpi", ["Toka-Leya"] = "dov", ["Tokano"] = "zuh", ["Tokelauan"] = "tkl", ["Toki Pona"] = "tok", ["Tokunoshima"] = "tkn", ["Tol"] = "jic", ["Tolai"] = "ksd", ["Tolaki"] = "lbw", ["Tolomako"] = "tlm", ["Tolowa"] = "tol", ["Toloza"] = "ytl", ["Toma"] = "tod", ["Tomadino"] = "tdi", ["Tombelala"] = "ttp", ["Tombonuo"] = "txa", ["Tombulu"] = "tom", ["Tomini"] = "txm", ["Tommeginne"] = "xpv", ["Tommo So"] = "dto", ["Tomo Kan Dogon"] = "dtm", ["Tomoip"] = "tqp", ["Tondano"] = "tdn", ["Tonga (Malawi)"] = "tog", ["Tonga (Mozambique)"] = "toh", ["Tonga (Zambia)"] = "toi", ["Tongan"] = "to", ["Tongwe"] = "tny", ["Tonjon"] = "tjn", ["Tonkawa"] = "tqw", ["Tonsawang"] = "tnw", ["Tonsea"] = "txs", ["Tontemboan"] = "tnt", ["Toogee"] = "xpx", ["Tooro"] = "ttj", ["Topoiyo"] = "toy", ["Toposa"] = "toq", ["Toraja-Sa'dan"] = "sda", ["Toram"] = "trj", ["Torau"] = "ttu", ["Toro"] = "tdv", ["Toro So Dogon"] = "dts", ["Toro Tegu Dogon"] = "dtt", ["Toromono"] = "tno", ["Torona"] = "tqr", ["Torres Strait Creole"] = "tcs", ["Torricelli"] = "tei", ["Torwali"] = "trw", ["Torá"] = "trz", ["Tosu"] = "sit-tos", ["Totela"] = "ttl", ["Toto"] = "txo", ["Totoli"] = "txe", ["Totomachapan Zapotec"] = "zph", ["Totontepec Mixe"] = "mto", ["Totoro"] = "ttk", ["Touo"] = "tqu", ["Toura"] = "neb", ["Towei"] = "ttn", ["Translingual"] = "mul", ["Transylvanian Saxon"] = "gmw-tsx", ["Traveller Danish"] = "rmd", ["Traveller Norwegian"] = "rmg", ["Traveller Scottish"] = "trl", ["Tregami"] = "trm", ["Tremembé"] = "tme", ["Trieng"] = "stg", ["Trimuris"] = "tip", ["Tring"] = "tgq", ["Tringgus"] = "trx", ["Trinidad and Tobago Sign Language"] = "lst", ["Trinidadian Creole English"] = "trf", ["Trinitario"] = "trn", ["Trió"] = "tri", ["Truká"] = "tka", ["Trumai"] = "tpy", ["Ts'ün-Lao"] = "tsl", ["Tsaangi"] = "tsa", ["Tsafiki"] = "cof", ["Tsakhur"] = "tkr", ["Tsakonian"] = "tsd", ["Tsakwambo"] = "kvz", ["Tsamai"] = "tsb", ["Tsat"] = "huq", ["Tsetsaut"] = "txc", ["Tsez"] = "ddo", ["Tshangla"] = "tsj", ["Tshobdun"] = "sit-tsh", ["Tshwa"] = "hio", ["Tsikimba"] = "kdl", ["Tsimané"] = "cas", ["Tsimshian"] = "tsi", ["Tsishingini"] = "tsw", ["Tso"] = "ldp", ["Tsogo"] = "tsv", ["Tsonga"] = "ts", ["Tsotsitaal"] = "fly", ["Tsou"] = "tsu", ["Tsucuba"] = "cbq", ["Tsum"] = "ttz", ["Tsuut'ina"] = "srs", ["Tsuvadi"] = "tvd", ["Tsuvan"] = "tsh", ["Tswa"] = "tsc", ["Tswana"] = "tn", ["Tswapong"] = "two", ["Tuamotuan"] = "pmt", ["Tuareg"] = "tmh", ["Tubar"] = "tbu", ["Tucano"] = "tuo", ["Tugen"] = "tuy", ["Tugun"] = "tzn", ["Tugutil"] = "tuj", ["Tuki"] = "bag", ["Tukpa"] = "tpq", ["Tukudede"] = "tkd", ["Tukumanféd"] = "tkf", ["Tula"] = "tul", ["Tule-Kaweah Yokuts"] = "yok-tky", ["Tulehu"] = "tlu", ["Tulishi"] = "tey", ["Tulu"] = "tcy", ["Tulu-Bohuai"] = "rak", ["Tulua"] = "aus-tul", ["Tuma-Irumu"] = "iou", ["Tumak"] = "tmc", ["Tumbuka"] = "tum", ["Tumi"] = "kku", ["Tumleo"] = "tmq", ["Tumshuqese"] = "xtq", ["Tumtum"] = "tbr", ["Tumulung Sisaala"] = "sil", ["Tundra Enets"] = "enh", ["Tundra Nenets"] = "yrk-tun", ["Tunen"] = "tvu", ["Tungag"] = "lcm", ["Tunggare"] = "trt", ["Tunia"] = "tug", ["Tunica"] = "tun", ["Tunisian Arabic"] = "aeb", ["Tunisian Berber"] = "sds", ["Tunisian Sign Language"] = "tse", ["Tunjung"] = "tjg", ["Tunni"] = "tqq", ["Tunumiisut"] = "esx-tut", ["Tunzu"] = "dza", ["Tuoba"] = "mis-tuo", ["Tuotomb"] = "ttf", ["Tuparí"] = "tpr", ["Tupinambá"] = "tpn", ["Tupinikin"] = "tpk", ["Tupuri"] = "tui", ["Turaka"] = "trh", ["Turdetanian"] = "mis-tdt", ["Turdulian"] = "mis-tdl", ["Turi"] = "trd", ["Turiwára"] = "twt", ["Turka"] = "tuz", ["Turkana"] = "tuv", ["Turkish"] = "tr", ["Turkish Sign Language"] = "tsm", ["Turkmen"] = "tk", ["Turks and Caicos Creole English"] = "tch", ["Turoyo"] = "tru", ["Turumsa"] = "tqm", ["Turung"] = "try", ["Tuscarora"] = "tus", ["Tutelo"] = "tta", ["Tutong"] = "ttg", ["Tutsa Naga"] = "tvt", ["Tutuba"] = "tmi", ["Tututepec Mixtec"] = "mtu", ["Tututni"] = "tuu", ["Tuvaluan"] = "tvl", ["Tuvan"] = "tyv", ["Tuwali Ifugao"] = "ifk", ["Tuwari"] = "tww", ["Tuwuli"] = "bov", ["Tuxináwa"] = "tux", ["Tuxá"] = "tud", ["Tuyuca"] = "tue", ["Tuyuhun"] = "mis-tuh", ["Twana"] = "twa", ["Twendi"] = "twn", ["Tyap"] = "kcg", ["Tyaraity"] = "woa", ["Tyerrernotepanner"] = "xph", ["Tz'utujil"] = "tzj", ["Tzeltal"] = "tzh", ["Tzotzil"] = "tzo", ["Tày"] = "tyz", ["Tày Tac"] = "tyt", ["Tây Bồi"] = "tas", ["Téén"] = "lor", ["Tübatulabal"] = "tub", ["U"] = "uuu", ["Uab Meto"] = "aoz", ["Uamué"] = "uam", ["Uare"] = "ksj", ["Ubaghara"] = "byc", ["Ubang"] = "uba", ["Ubi"] = "ubi", ["Ubir"] = "ubr", ["Ubykh"] = "uby", ["Ucayali-Yurúa Ashéninka"] = "cpb", ["Uda"] = "uda", ["Udi"] = "udi", ["Udihe"] = "ude", ["Udmurt"] = "udm", ["Uduk"] = "udu", ["Ufim"] = "ufi", ["Ugandan Sign Language"] = "ugn", ["Ugaritic"] = "uga", ["Ughele"] = "uge", ["Uhami"] = "uha", ["Uisai"] = "uis", ["Ujir"] = "udj", ["Ukaan"] = "kcf", ["Ukhwejo"] = "ukh", ["Ukit"] = "umi", ["Ukpe-Bayobiri"] = "ukp", ["Ukpet-Ehom"] = "akd", ["Ukrainian"] = "uk", ["Ukrainian Sign Language"] = "ukl", ["Ukue"] = "uku", ["Ukuriguma"] = "ukg", ["Ukwa"] = "ukq", ["Ukwuani-Aboh-Ndoni"] = "ukw", ["Ulau-Suain"] = "svb", ["Ulch"] = "ulc", ["Uldeme"] = "udl", ["Ulithian"] = "uli", ["Ullatan"] = "ull", ["Ulumanda'"] = "ulm", ["Ulwa (New Guinea)"] = "yla", ["Ulwa (Nicaragua)"] = "ulw", ["Uma"] = "ppk", ["Uma' Lasan"] = "xky", ["Uma' Lung"] = "ulu", ["Umanakaina"] = "gdn", ["Umatilla"] = "uma", ["Umbindhamu"] = "umd", ["Umbrian"] = "xum", ["Umbu-Ungu"] = "ubu", ["Umbugarla"] = "umr", ["Umbundu"] = "umb", ["Umbuygamu"] = "umg", ["Ume Sami"] = "sju", ["Umeda"] = "upi", ["Umiida"] = "xud", ["Umiray Dumaget Agta"] = "due", ["Umon"] = "umm", ["Umotína"] = "umo", ["Umpila"] = "ump", ["Una"] = "mtg", ["Unami"] = "unm", ["Unde Kaili"] = "unz", ["Undetermined"] = "und", ["Uneapa"] = "bbn", ["Uneme"] = "une", ["Unggaranggu"] = "xun", ["Unggumi"] = "xgu", ["Uni"] = "uni", ["Unserdeutsch"] = "uln", ["Unua"] = "onu", ["Unubahe"] = "unu", ["Uokha"] = "uok", ["Upper Chehalis"] = "cjh", ["Upper Grand Valley Dani"] = "dna", ["Upper Kinabatangan"] = "dmg", ["Upper Kuskokwim"] = "kuu", ["Upper Mandobo"] = "aax", ["Upper Necaxa Totonac"] = "tku", ["Upper Sorbian"] = "hsb", ["Upper Ta'oih"] = "tth", ["Upper Tanana"] = "tau", ["Upper Taromi"] = "tov", ["Upper Umpqua"] = "xup", ["Ura (New Guinea)"] = "uro", ["Ura (Vanuatu)"] = "uur", ["Uradhi"] = "urf", ["Urak Lawoi'"] = "urk", ["Urali"] = "url", ["Urapmin"] = "urm", ["Urarina"] = "ura", ["Urartian"] = "xur", ["Urat"] = "urt", ["Urdu"] = "ur", ["Urhobo"] = "urh", ["Uri"] = "uvh", ["Urigina"] = "urg", ["Urim"] = "uri", ["Urimo"] = "urx", ["Urningangg"] = "urc", ["Uru"] = "ure", ["Uru-Eu-Wau-Wau"] = "urz", ["Uru-Pa-In"] = "urp", ["Uruangnirin"] = "urn", ["Uruava"] = "urv", ["Urubú-Kaapor"] = "urb", ["Uruguayan Sign Language"] = "ugy", ["Urum"] = "uum", ["Urumi"] = "uru", ["Usaghade"] = "usk", ["Usan"] = "wnu", ["Usarufa"] = "usa", ["Ushojo"] = "ush", ["Usila Chinantec"] = "cuc", ["Uspanteco"] = "usp", ["Usui"] = "usi", ["Utarmbung"] = "omo", ["Ute"] = "ute", ["Utu"] = "utu", ["Uvbie"] = "evh", ["Uwinymil"] = "aus-uwi", ["Uya"] = "usu", ["Uyajitaya"] = "duk", ["Uyghur"] = "ug", ["Uzbek"] = "uz", ["Uzbeki Arabic"] = "auz", ["Uzekwe"] = "eze", ["Vaagri Booli"] = "vaa", ["Vaghri"] = "vgr", ["Vaghua"] = "tva", ["Vagla"] = "vag", ["Vai"] = "vai", ["Vaiphei"] = "vap", ["Vale"] = "vae", ["Valencian Sign Language"] = "vsv", ["Valle Nacional Chinantec"] = "cvn", ["Valley Maidu"] = "vmv", ["Valman"] = "van", ["Valpei"] = "vlp", ["Vamale"] = "mkt", ["Vame"] = "mlr", ["Vandalic"] = "xvn", ["Vangunu"] = "mpr", ["Vanimo"] = "vam", ["Vanji"] = "ira-wnj", ["Vanuma"] = "vau", ["Vao"] = "vao", ["Varhadi"] = "vah", ["Varisi"] = "vrs", ["Varli"] = "vav", ["Vasavi"] = "vas", ["Vayu"] = "vay", ["Veddah"] = "ved", ["Vehes"] = "val", ["Vemgo-Mabas"] = "vem", ["Venda"] = "ve", ["Venetan"] = "vec", ["Venetic"] = "xve", ["Venezuelan Sign Language"] = "vsl", ["Ventureño"] = "veo", ["Veps"] = "vep", ["Vera'a"] = "vra", ["Vestinian"] = "xvs", ["Vidunda"] = "vid", ["Viemo"] = "vig", ["Vietnamese"] = "vi", ["Vilamovian"] = "wym", ["Vilela"] = "vil", ["Vili"] = "vif", ["Vincentian Creole English"] = "svc", ["Vinitiri"] = "vmg", ["Virgin Islands Creole"] = "vic", ["Vishavan"] = "vis", ["Viti"] = "vit", ["Vitou"] = "vto", ["Viya"] = "gev", ["Vlax Romani"] = "rmy", ["Volapük"] = "vo", ["Volga German"] = "gmw-vog", ["Volscian"] = "xvo", ["Vono"] = "kch", ["Voro"] = "vor", ["Votic"] = "vot", ["Vumbu"] = "vum", ["Vunapu"] = "vnp", ["Vunjo"] = "vun", ["Vurës"] = "msn", ["Vute"] = "vut", ["Võro"] = "vro", ["Wa"] = "wbm", ["Wa'ema"] = "wag", ["Waama"] = "wwa", ["Waamwang"] = "wmn", ["Wab"] = "wab", ["Wabo"] = "wbb", ["Waboda"] = "kmx", ["Waci Gbe"] = "wci", ["Wadaginam"] = "wdg", ["Waddar"] = "wbq", ["Wadi Wadi"] = "xwd", ["Wadiyara Koli"] = "kxp", ["Wadjabangayi"] = "wdy", ["Wadjiginy"] = "wdj", ["Wadjigu"] = "wdu", ["Wae Rana"] = "wrx", ["Waffa"] = "waj", ["Wagawaga"] = "wgb", ["Wagaya"] = "wga", ["Wagdi"] = "wbr", ["Wageman"] = "waq", ["Wagi"] = "fad", ["Wahau Kayan"] = "whu", ["Wahau Kenyah"] = "whk", ["Wahgi"] = "wgi", ["Waigali"] = "wbk", ["Waigeo"] = "wgo", ["Waikuri"] = "nai-wai", ["Wailaki"] = "wlk", ["Wailapa"] = "wlr", ["Waima'a"] = "wmh", ["Waimaha"] = "bao", ["Waimiri-Atroari"] = "atr", ["Wainumá"] = "awd-wai", ["Waioli"] = "wli", ["Waitaká"] = "sai-wai", ["Waiwai"] = "waw", ["Waja"] = "wja", ["Wajarri"] = "wbv", ["Wajuk"] = "xwj", ["Waka"] = "wav", ["Wakawaka"] = "wkw", ["Wakhi"] = "wbl", ["Wakoná"] = "waf", ["Wala"] = "lgl", ["Walak"] = "wlw", ["Walangama"] = "nlw", ["Wali (Ghana)"] = "wlx", ["Wali (Sudan)"] = "wll", ["Waling"] = "wly", ["Walio"] = "wla", ["Walla Walla"] = "waa", ["Wallisian"] = "wls", ["Walloon"] = "wa", ["Walmajarri"] = "wmt", ["Wam"] = "wmo", ["Wamas"] = "wmc", ["Wambaya"] = "wmb", ["Wambon"] = "wms", ["Wambule"] = "wme", ["Wamey"] = "cou", ["Wamin"] = "wmi", ["Wampar"] = "lbq", ["Wampur"] = "waz", ["Wan"] = "wan", ["Wanap"] = "wnp", ["Wancho"] = "nnp", ["Wanda"] = "wbh", ["Wandala"] = "mfi", ["Wandamen"] = "wad", ["Wandarang"] = "wnd", ["Wandji"] = "wdd", ["Waneci"] = "wne", ["Wanga"] = "lwg", ["Wanggamala"] = "wnm", ["Wangganguru"] = "wgg", ["Wanggom"] = "wng", ["Wangkayutyuru"] = "wky", ["Wangkumara"] = "xwk", ["Wanham"] = "sai-wnm", ["Wanji"] = "wbi", ["Wanman"] = "wbt", ["Wannu"] = "jub", ["Wano"] = "wno", ["Wantoat"] = "wnc", ["Wanukaka"] = "wnk", ["Wanyi"] = "wny", ["Wané"] = "hwa", ["Wapan"] = "juk", ["Wapishana"] = "wap", ["Wappo"] = "wao", ["War-Jaintia"] = "aml", ["Wara"] = "wbf", ["Warao"] = "wba", ["Warapu"] = "wra", ["Waray Sorsogon"] = "srv", ["Waray-Waray"] = "war", ["Wardaman"] = "wrr", ["Wardandi"] = "wxw", ["Warembori"] = "wsa", ["Wari'"] = "pav", ["Waris"] = "wrs", ["Waritai"] = "wbe", ["Wariyangga"] = "wri", ["Warji"] = "wji", ["Warkay-Bipim"] = "bgv", ["Warlmanpa"] = "wrl", ["Warlpiri"] = "wbp", ["Warluwara"] = "wrb", ["Warnang"] = "wrn", ["Waropen"] = "wrp", ["Warray"] = "wrz", ["Warrgamay"] = "wgy", ["Warrwa"] = "wwr", ["Waru"] = "wru", ["Warumungu"] = "wrm", ["Waruna"] = "wrv", ["Warungu"] = "wrg", ["Warwar Feni"] = "hrw", ["Wasco-Wishram"] = "wac", ["Wasembo"] = "gsp", ["Washo"] = "was", ["Waskia"] = "wsk", ["Wastek"] = "hus", ["Wasu"] = "wsu", ["Watakataui"] = "wtk", ["Watam"] = "wax", ["Wathaurong"] = "wth", ["Watiwa"] = "wtf", ["Watubela"] = "wah", ["Waube"] = "kop", ["Wauja"] = "wau", ["Wauyai"] = "wuy", ["Wawa"] = "www", ["Wawonii"] = "wow", ["Waxiang"] = "wxa", ["Wayampi"] = "oym", ["Wayana"] = "way", ["Wayanad Chetti"] = "ctt", ["Wayoró"] = "wyr", ["Wayumara"] = "sai-way", ["Wayuu"] = "guc", ["Wedau"] = "wed", ["Weh"] = "weh", ["Welaung"] = "weu", ["Weliki"] = "klh", ["Welsh"] = "cy", ["Welsh Romani"] = "rmw", ["Wemale"] = "weo", ["Wemba-Wemba"] = "xww", ["Weme Gbe"] = "wem", ["Wendat"] = "wdt", ["Weri"] = "wer", ["Wersing"] = "kvw", ["West Ambae"] = "nnd", ["West Central Banda"] = "bbp", ["West Circassian"] = "ady", ["West Coast Bajau"] = "bdr", ["West Damar"] = "drn", ["West Flemish"] = "vls", ["West Frisian"] = "fy", ["West Greenlandic Pidgin"] = "crp-gep", ["West Lembata"] = "lmj", ["West Makian"] = "mqs", ["West Masela"] = "mss", ["West Miraya Bikol"] = "fbl", ["West Tarangan"] = "txn", ["West Uvean"] = "uve", ["West-Central Limba"] = "lia", ["Western Apache"] = "apw", ["Western Arrernte"] = "are", ["Western Bolivian Guarani"] = "gnw", ["Western Bru"] = "brv", ["Western Bukidnon Manobo"] = "mbb", ["Western Cham"] = "cja", ["Western Dani"] = "dnw", ["Western Durango Nahuatl"] = "azn", ["Western Fijian"] = "wyy", ["Western Highland Chatino"] = "ctp", ["Western Huasteca Nahuatl"] = "nhw", ["Western Jicaque"] = "nai-wji", ["Western Juxtlahuaca Mixtec"] = "jmx", ["Western Karaboro"] = "kza", ["Western Katu"] = "kuf", ["Western Kayah"] = "kyu", ["Western Keres"] = "kjq", ["Western Krahn"] = "krw", ["Western Lalu"] = "ywl", ["Western Lawa"] = "lcp", ["Western Magar"] = "mrd", ["Western Maninkakan"] = "mlq", ["Western Mari"] = "mrj", ["Western Mashan Hmong"] = "hmw", ["Western Meohang"] = "raf", ["Western Muria"] = "mut", ["Western Neo-Aramaic"] = "amw", ["Western Ojibwa"] = "ojw", ["Western Parbate Kham"] = "kjl", ["Western Penan"] = "pne", ["Western Pwo"] = "pwo", ["Western Sisaala"] = "ssl", ["Western Subanon"] = "suc", ["Western Tamang"] = "tdg", ["Western Tawbuid"] = "twb", ["Western Totonac"] = "tqt", ["Western Tunebo"] = "tnb", ["Western Xiangxi Miao"] = "mmr", ["Western Xwla Gbe"] = "xwl", ["Western Yugur"] = "ybe", ["Wewaw"] = "wea", ["Weyewa"] = "wew", ["Weyto"] = "woy", ["White Gelao"] = "giw", ["White Hmong"] = "mww", ["White Lachi"] = "lwh", ["Whitesands"] = "tnp", ["Wiarumus"] = "tua", ["Wichita"] = "wic", ["Wichí Lhamtés Güisnay"] = "mzh", ["Wichí Lhamtés Nocten"] = "mtp", ["Wichí Lhamtés Vejoz"] = "wlv", ["Wik-Epa"] = "wie", ["Wik-Iiyanh"] = "wij", ["Wik-Keyangan"] = "wif", ["Wik-Me'anha"] = "wih", ["Wik-Mungkan"] = "wim", ["Wik-Ngathana"] = "wig", ["Wikalkan"] = "wik", ["Wikngenchera"] = "wua", ["Wilawila"] = "wil", ["Winnebago"] = "win", ["Wintu"] = "wnw", ["Winyé"] = "kst", ["Wipi"] = "gdr", ["Wiradjuri"] = "wrh", ["Wiraféd"] = "wir", ["Wirangu"] = "wgu", ["Wiru"] = "wiu", ["Wirö"] = "wpc", ["Wiwa"] = "mbp", ["Wiyot"] = "wiy", ["Wobé"] = "wob", ["Woccon"] = "xwc", ["Wogamusin"] = "wog", ["Wogeo"] = "woc", ["Woi"] = "wbw", ["Woiwurrung"] = "wyi", ["Wojenaka"] = "jod", ["Wolane"] = "wle", ["Wolani"] = "wod", ["Wolaytta"] = "wal", ["Woleaian"] = "woe", ["Wolio"] = "wlo", ["Wolof"] = "wo", ["Womo"] = "wmx", ["Wong-gie"] = "aus-won", ["Wongo"] = "won", ["Woods Cree"] = "cwd", ["Woria"] = "wor", ["Worimi"] = "kda", ["Worodougou"] = "jud", ["Worora"] = "wro", ["Wotapuri-Katarqalai"] = "wsv", ["Wotu"] = "wtw", ["Woun Meu"] = "noa", ["Written Oirat"] = "xwo", ["Wu"] = "wuu", ["Wudu"] = "wud", ["Wuhuan"] = "mis-wuh", ["Wulguru"] = "aus-wul", ["Wuliwuli"] = "wlu", ["Wulna"] = "wux", ["Wumboko"] = "bqm", ["Wumbvu"] = "wum", ["Wumeng"] = "ywu", ["Wunai Bunu"] = "bwn", ["Wunambal"] = "wub", ["Wurrugu"] = "wur", ["Wusa"] = "yig", ["Wushi"] = "bse", ["Wusi"] = "wsi", ["Wutung"] = "wut", ["Wutunhua"] = "wuh", ["Wuvulu-Aua"] = "wuv", ["Wyandot"] = "wya", ["Wára"] = "tci", ["Wãpha"] = "juw", ["Wè Southern"] = "gxx", ["Wè Western"] = "wec", ["Xadani Zapotec"] = "zax", ["Xakriabá"] = "xkr", ["Xamtanga"] = "xan", ["Xanaguía Zapotec"] = "ztg", ["Xavante"] = "xav", ["Xerénte"] = "xer", ["Xetá"] = "xet", ["Xhosa"] = "xh", ["Xianbei"] = "mis-xbi", ["Xiang"] = "hsn", ["Xibe"] = "sjo", ["Xicotepec de Juárez Totonac"] = "too", ["Xinca"] = "xin", ["Xingú Asuriní"] = "asn", ["Xiongnu"] = "mis-xnu", ["Xipaya"] = "xiy", ["Xiri"] = "xii", ["Xiriâna"] = "xir", ["Xishanba Lalo"] = "ywt", ["Xocó"] = "sai-xoc", ["Xokleng"] = "xok", ["Xukurú"] = "xoo", ["Xwela Gbe"] = "xwe", ["Xârâcùù"] = "ane", ["Xârâgurè"] = "axx", ["Yaa"] = "iyx", ["Yaaku"] = "muu", ["Yabarana"] = "yar", ["Yabaâna"] = "ybn", ["Yaben"] = "ybm", ["Yabong"] = "ybo", ["Yabula Yabula"] = "yxy", ["Yace"] = "ekr", ["Yaeyama"] = "rys", ["Yafi"] = "wfg", ["Yagara"] = "yxg", ["Yagaria"] = "ygr", ["Yaghnobi"] = "yai", ["Yagomi"] = "ygm", ["Yagua"] = "yad", ["Yagwoia"] = "ygw", ["Yahadian"] = "ner", ["Yahang"] = "rhp", ["Yahuna"] = "ynu", ["Yaka"] = "yaf", ["Yakaikeke"] = "ykk", ["Yakan"] = "yka", ["Yakima"] = "yak", ["Yakkha"] = "ybh", ["Yakoma"] = "yky", ["Yakut"] = "sah", ["Yala"] = "yba", ["Yalahatan"] = "jal", ["Yalakalore"] = "xyl", ["Yalarnnga"] = "ylr", ["Yale"] = "nce", ["Yaleba"] = "ylb", ["Yalunka"] = "yal", ["Yalálag Zapotec"] = "zpu", ["Yamap"] = "ymp", ["Yamba"] = "yam", ["Yamben"] = "ynb", ["Yambes"] = "ymb", ["Yambeta"] = "yat", ["Yamdena"] = "jmd", ["Yameo"] = "yme", ["Yami"] = "tao", ["Yaminahua"] = "yaa", ["Yamongeri"] = "ymg", ["Yamphu"] = "ybi", ["Yan-nhangu"] = "jay", ["Yana"] = "ynn", ["Yanda"] = "yda", ["Yanda Dogon"] = "dym", ["Yandjibara"] = "xyb", ["Yandruwandha"] = "ynd", ["Yanesha'"] = "ame", ["Yangben"] = "yav", ["Yangkaal"] = "aus-ynk", ["Yangkam"] = "bsx", ["Yangman"] = "jng", ["Yango"] = "yng", ["Yangulam"] = "ynl", ["Yangum Dey"] = "yde", ["Yangum Gel"] = "ygl", ["Yangum Mon"] = "ymo", ["Yankunytjatjara"] = "kdd", ["Yanomam"] = "wca", ["Yanomamö"] = "guu", ["Yansi"] = "yns", ["Yanyuwa"] = "jao", ["Yao (Africa)"] = "yao", ["Yao (South America)"] = "sai-yao", ["Yaosakor Asmat"] = "asy", ["Yaouré"] = "yre", ["Yapese"] = "yap", ["Yaqay"] = "jaq", ["Yaqui"] = "yaq", ["Yarawata"] = "yrw", ["Yareba"] = "yrb", ["Yareni Zapotec"] = "zae", ["Yarli"] = "yxl", ["Yarluyandi"] = "yry", ["Yarumá"] = "sai-yar", ["Yarí"] = "yri", ["Yasa"] = "yko", ["Yatay"] = "yty", ["Yatee Zapotec"] = "zty", ["Yatzachi Zapotec"] = "zav", ["Yau (Finisterre)"] = "yuw", ["Yau (Torricelli)"] = "yyu", ["Yaur"] = "jau", ["Yautepec Zapotec"] = "zpb", ["Yavitero"] = "yvt", ["Yawa"] = "yva", ["Yawalapití"] = "yaw", ["Yawanawa"] = "ywn", ["Yawarawarga"] = "yww", ["Yaweyuha"] = "yby", ["Yawijibaya"] = "jbw", ["Yawiyo"] = "ybx", ["Yawuru"] = "ywr", ["Yaygir"] = "xya", ["Yazghulami"] = "yah", ["Ye'kwana"] = "mch", ["Yei"] = "jei", ["Yekhee"] = "ets", ["Yekora"] = "ykr", ["Yele"] = "yle", ["Yelmek"] = "jel", ["Yelogu"] = "ylg", ["Yemaek"] = "hmk", ["Yemba"] = "ybb", ["Yemeni Arabic"] = "ayn", ["Yemsa"] = "jnj", ["Yendang"] = "yen", ["Yeni"] = "yei", ["Yenish"] = "yec", ["Yerakai"] = "yra", ["Yeretuar"] = "gop", ["Yeri"] = "yev", ["Yerong"] = "yrn", ["Yerukula"] = "yeu", ["Yeskwa"] = "yes", ["Yessan-Mayo"] = "yss", ["Yetfa"] = "yet", ["Yevanic"] = "yej", ["Yeyi"] = "yey", ["Yiddish"] = "yi", ["Yidgha"] = "ydg", ["Yidiny"] = "yii", ["Yil"] = "yll", ["Yilan Creole"] = "ycr", ["Yimas"] = "yee", ["Yimchungru Naga"] = "yim", ["Yinbaw Karen"] = "kvu", ["Yinchia"] = "yin", ["Yindjibarndi"] = "yij", ["Yindjilandji"] = "yil", ["Yine"] = "pib", ["Yinggarda"] = "yia", ["Yinhawangka"] = "ywg", ["Yiningayi"] = "ygi", ["Yintale Karen"] = "kvy", ["Yinwum"] = "yxm", ["Yir-Yoront"] = "yiy", ["Yirandali"] = "ljw", ["Yis"] = "yis", ["Yitha Yitha"] = "xth", ["Yoba"] = "yob", ["Yocoboué Dida"] = "gud", ["Yogad"] = "yog", ["Yoidik"] = "ydk", ["Yoke"] = "yki", ["Yola"] = "yol", ["Yolmo"] = "scp", ["Yolngu Sign Language"] = "ygs", ["Yoloxochitl Mixtec"] = "xty", ["Yom"] = "pil", ["Yombe"] = "yom", ["Yonaguni"] = "yoi", ["Yong"] = "yno", ["Yongkom"] = "yon", ["Yopno"] = "yut", ["Yora"] = "mts", ["Yoron"] = "yox", ["Yorta Yorta"] = "xyy", ["Yoruba"] = "yo", ["Yosondúa Mixtec"] = "mpm", ["Youle Jino"] = "jiu", ["Younuo Bunu"] = "buh", ["Yout Wam"] = "ytw", ["Yoy"] = "yoy", ["Yuanga"] = "nua", ["Yucatec Maya"] = "yua", ["Yucatec Maya Sign Language"] = "msd", ["Yuchi"] = "yuc", ["Yucuañe Mixtec"] = "mvg", ["Yucuna"] = "ycn", ["Yug"] = "yug", ["Yugambal"] = "yub", ["Yugoslavian Sign Language"] = "ysl", ["Yugul"] = "ygu", ["Yuhup"] = "yab", ["Yuki"] = "yuk", ["Yukpa"] = "yup", ["Yukuben"] = "ybl", ["Yulu"] = "yul", ["Yuma"] = "yum", ["Yumana"] = "awd-yum", ["Yup'ik"] = "esu", ["Yupiltepeque"] = "nai-yup", ["Yupua"] = "sai-yup", ["Yuqui"] = "yuq", ["Yuracare"] = "yuz", ["Yuri"] = "sai-yri", ["Yurok"] = "yur", ["Yuru"] = "ljx", ["Yurumanguí"] = "sai-yur", ["Yurutí"] = "yui", ["Yutanduchi Mixtec"] = "mab", ["Yuwana"] = "yau", ["Yuyu"] = "yxu", ["Yámana"] = "yag", ["Zaachila Zapotec"] = "ztx", ["Zabana"] = "kji", ["Zacatepec Chatino"] = "ctz", ["Zacatlán-Ahuacatlán-Tepetzintla Nahuatl"] = "nhi", ["Zaghawa"] = "zag", ["Zaiwa"] = "atb", ["Zakhring"] = "zkr", ["Zambian Sign Language"] = "zsl", ["Zan Gula"] = "zna", ["Zanaki"] = "zak", ["Zande"] = "zne", ["Zangskari"] = "zau", ["Zangwal"] = "zah", ["Zaniza Zapotec"] = "zpw", ["Zapotec"] = "zap", ["Zaramo"] = "zaj", ["Zari"] = "zaz", ["Zarma"] = "dje", ["Zauzou"] = "zal", ["Zay"] = "zwa", ["Zayein Karen"] = "kxk", ["Zayse-Zergulla"] = "zay", ["Zazaki"] = "zza", ["Zazao"] = "jaj", ["Zbu"] = "sit-zbu", ["Zealandic"] = "zea", ["Zeem"] = "zua", ["Zemba"] = "dhm", ["Zeme Naga"] = "nzm", ["Zenag"] = "zeg", ["Zenaga"] = "zen", ["Zenzontepec Chatino"] = "czn", ["Zhaba"] = "zhb", ["Zhang-Zhung"] = "xzh", ["Zhenan Min"] = "nan-zhe", ["Zhire"] = "zhi", ["Zhoa"] = "zhw", ["Zhuang"] = "za", ["Zhár"] = "jjr", ["Zia"] = "zia", ["Zialo"] = "zil", ["Zigula"] = "ziw", ["Zimakani"] = "zik", ["Zimba"] = "zmb", ["Zimbabwe Sign Language"] = "zib", ["Zinza"] = "zin", ["Zipser German"] = "gmw-zps", ["Zirenkel"] = "zrn", ["Ziriya"] = "zir", ["Zizilivakan"] = "ziz", ["Zo'é"] = "pto", ["Zokhuo"] = "yzk", ["Zoogocho Zapotec"] = "zpq", ["Zotung Chin"] = "czt", ["Zou"] = "zom", ["Zulgo-Gemzek"] = "gnd", ["Zulu"] = "zu", ["Zumaya"] = "zuy", ["Zumbun"] = "jmb", ["Zuni"] = "zun", ["Zuojiang Zhuang"] = "zzj", ["Zuwara"] = "ber-zuw", ["Zyphe"] = "zyp", ["Záparo"] = "zro", ["Àhàn"] = "ahn", ["Áncá"] = "acb", ["Äiwoo"] = "nfl", ["Äynu"] = "aib", ["Ömie"] = "aom", ["Önge"] = "oon", ["ǀXam"] = "xam", ["ǁAni"] = "hnh", ["ǁGana"] = "gnk", ["ǁXegwi"] = "xeg", ["ǂHoan"] = "huc", ["ǃKung"] = "khi-kun", ["ǃXóõ"] = "nmn", ["Ỹaroamë"] = "yro", } 5uphgjvduyt9kbn9gxndgn1gnu14knj Module:maintenance category 828 8011 54762 2026-09-25T22:10:50Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local pages_module = "Module:pages" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local new_title = mw.title.new local uses_hidden_category -- Defined below. --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no...' 54762 Scribunto text/plain local export = {} local pages_module = "Module:pages" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local new_title = mw.title.new local uses_hidden_category -- Defined below. --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function is_sandbox(...) is_sandbox = require(pages_module).is_sandbox return is_sandbox(...) end local function is_testcase_page(...) is_testcase_page = require(pages_module).is_testcase_page return is_testcase_page(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local current_title local function get_current_title() current_title, get_current_title = mw.title.getCurrentTitle(), nil return current_title end function export.uses_hidden_category(title) local namespace = title.namespace -- Thread: and Summary: pages are named "Thread:PAGE" or "Summary:PAGE", -- where PAGE is the page they relate to. How we treat them therefore -- depends on what that page is. while namespace == 90 or namespace == 92 do title = new_title(title.text) namespace = title.namespace end -- User: and all talk namespaces, as well as all sandboxes and testcase pages (including their documentation pages). return ( namespace == 2 or title.isTalkPage or is_sandbox(title, "include documentation") or is_testcase_page(title, "include documentation") ) and true or false end uses_hidden_category = export.uses_hidden_category function export.get_category(name, cat) if uses_hidden_category(current_title or get_current_title()) then name = name .. "/hidden" end return cat and format_categories(name, nil, "-", nil, true) or name end function export.template(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, ["cat"] = {type = "boolean", default = false} }) return export.get_category(args[1], args.cat) end return export 0k9xe0i9582irnmc4uhevzdi2o9uso3 Module:string/compare 828 8012 54763 2026-09-25T22:11:23Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local byte = string.byte local match = string.match local sub = string.sub --[==[ A comparison function for strings, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. This function always sorts using byte-order, which makes it roughly equivalent to the {<} operator, but with fixes for two serious bugs raised in [[phab:T193096#4161287]] and [[phab:T49137#9167559]]: * {<} is supposed to comp...' 54763 Scribunto text/plain local byte = string.byte local match = string.match local sub = string.sub --[==[ A comparison function for strings, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. This function always sorts using byte-order, which makes it roughly equivalent to the {<} operator, but with fixes for two serious bugs raised in [[phab:T193096#4161287]] and [[phab:T49137#9167559]]: * {<} is supposed to compare UTF-8 codepoints in the two strings, but when a codepoint that is U+10000 or above is encountered in the left-hand string, {<} always returns {false}, irrespective of the content of the other string. * {<} treats unassigned codepoints and non-UTF-8 byte sequences as being higher than {"\0"} but lower than {"\1"}, instead of sorting according to byte order.]==] return function(a, b) -- Equality check. if a == b then return false end -- Byte comparison is slow, so only do it when it's really needed: -- iterate over both strings, grabbing a set of ASCII bytes followed by -- a set of non-ASCII bytes from each (either of which could be empty), -- and compare them with ==. If the ASCII substrings are unequal, just -- use <, since the bug won't affect it. Otherwise, compare bytes in the -- non-ASCII substrings. local loc, ascii_a, nonascii_a, ascii_b, nonascii_b = 1 repeat ascii_a, nonascii_a = match(a, "^([^\128-\255]*)([\128-\255]*)", loc) ascii_b, nonascii_b, loc = match(b, "^([^\128-\255]*)([\128-\255]*)()", loc) -- update `loc` on the second call -- When comparing ASCII sets, use <. The lower substring will be -- from the lower string *except* when it comprises the start of the -- other substring and is followed by a non-ASCII character. For -- instance, if `ascii_a` is "pqrs": -- If `ascii_b` is "abc", `b` is lower, since "abc" < "pqrs". -- If `ascii_b` is "pqr" and followed by non-ASCII "ž", `a` is -- lower, since "pqrs" < "pqrž". -- If `ascii_b` is "pqr" and at the end of `b`, `b` is lower, since -- "pqr" < "pqrs". if ascii_a ~= ascii_b then if ascii_a < ascii_b then return nonascii_a == "" or ascii_a ~= sub(ascii_b, 1, #ascii_a) end return not (nonascii_b == "" or ascii_b ~= sub(ascii_a, 1, #ascii_b)) end -- If the non-ASCII parts are not equal, terminate the loop. until nonascii_a ~= nonascii_b -- If either one is the empty string, then the end of that string has -- been reached, making it the lower string. if nonascii_a == "" then return true elseif nonascii_b == "" then return false end loc = 1 while true do -- 4 bytes at a time is a balance between minimizing the number of -- byte() calls without grabbing unnecessary extra bytes after the -- difference. local b_a1, b_a2, b_a3, b_a4 = byte(nonascii_a, loc, loc + 3) if b_a1 == nil then return true end local b_b1, b_b2, b_b3, b_b4 = byte(nonascii_b, loc, loc + 3) if b_a1 ~= b_b1 then return b_b1 and b_a1 < b_b1 elseif b_a2 ~= b_b2 then return b_a2 == nil or b_b2 and b_a2 < b_b2 elseif b_a3 ~= b_b3 then return b_a3 == nil or b_b3 and b_a3 < b_b3 elseif b_a4 ~= b_b4 then return b_a4 == nil or b_b4 and b_a4 < b_b4 end loc = loc + 4 end end fshgty7mojs150t5jnkyy2f1rbcgty2 Module:string/decodeEntities 828 8013 54764 2026-09-25T22:11:48Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local load_module = "Module:load" local string_char_module = "Module:string/char" local find = string.find local gsub = string.gsub local match = string.match local require = require local tonumber = tonumber local function u(...) u = require(string_char_module) return u(...) end local entities local function get_entities() entities, get_entities = require(load_module).load_data("Module:data/entities"), nil return entities end local function decode_entity...' 54764 Scribunto text/plain local load_module = "Module:load" local string_char_module = "Module:string/char" local find = string.find local gsub = string.gsub local match = string.match local require = require local tonumber = tonumber local function u(...) u = require(string_char_module) return u(...) end local entities local function get_entities() entities, get_entities = require(load_module).load_data("Module:data/entities"), nil return entities end local function decode_entity(hash, x, code) -- "#" isn't included in "[%w\128-\255]", so if no "#" is found then it's a -- a named entity or a false match. if hash == "" then return (entities or get_entities())[x .. code] end -- Exclude numbers that don't fit the expected format. local cp if x == "" then cp = match(code, "^()%d+$") and tonumber(code) else cp = match(code, "^()%x+$") and tonumber(code, 16) end -- Exclude surrogates (U+D800 to U+DFFF) and codepoints that are too high. return cp and ( cp <= 0xD7FF or cp >= 0xE000 and cp <= 0x10FFFF ) and u(cp) or nil end return function(str) -- As an optimisation, only do a full search with gsub() if plain searches -- for "&" and ";" find anything. local amp = find(str, "&", nil, true) -- Search for ";" after the point "&" was found. return amp and find(str, ";", amp + 1, true) and -- Non-ASCII characters aren't valid in proper HTML named entities, but -- MediaWiki uses them in some nonstandard aliases (which have also been -- included in [[Module:data/entities]]), so include them anyway. gsub(str, "&(#?)([xX]?)([%w\128-\255]+);", decode_entity) or str end 1ehplvn1q6ksitguquhvf1khnaq3mxd Module:string/removeComments 828 8014 54765 2026-09-25T22:12:16Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local concat = table.concat local find = string.find local sub = string.sub local function remove_comments(str, pre) local pos1 = find(str, "<!--", nil, true) if not pos1 then return str end local pos2 = find(str, "-->", pos1 + 4, true) if not pos2 then return pre and sub(str, 1, pos1 - 1) or str end local ret = sub(str, 1, pos1 - 1) pos1 = pos2 + 3 pos2 = find(str, "<!--", pos1, true) if not pos2 then return ret .. sub(str, pos1) end local pos...' 54765 Scribunto text/plain local concat = table.concat local find = string.find local sub = string.sub local function remove_comments(str, pre) local pos1 = find(str, "<!--", nil, true) if not pos1 then return str end local pos2 = find(str, "-->", pos1 + 4, true) if not pos2 then return pre and sub(str, 1, pos1 - 1) or str end local ret = sub(str, 1, pos1 - 1) pos1 = pos2 + 3 pos2 = find(str, "<!--", pos1, true) if not pos2 then return ret .. sub(str, pos1) end local pos3 = find(str, "-->", pos2 + 4, true) if not pos3 then return ret .. sub(str, pos1, pre and pos2 - 1 or nil) end ret = {ret, sub(str, pos1, pos2 - 1)} local n = 2 while true do pos1 = pos3 + 3 pos2 = find(str, "<!--", pos1, true) if not pos2 then return concat(ret) .. sub(str, pos1) end pos3 = find(str, "-->", pos2 + 4, true) if not pos3 then return concat(ret) .. sub(str, pos1, pre and pos2 - 1 or nil) end n = n + 1 ret[n] = sub(str, pos1, pos2 - 1) end end --[==[Removes any HTML comments from the input text. `stage` can be one of three options: * {"PRE"} (default) applies the method used by MediaWiki's preprocessor: all {{code|html|<nowiki><!-- ... --></nowiki>}} pairs are removed, as well as any text after an unclosed {{code|html|<nowiki><!--</nowiki>}}. This is generally suitable when parsing raw template or [[mw:Parser extension tags|parser extension tag]] code. (Note, however, that the actual method used by the preprocessor is considerably more complex and differs under certain conditions (e.g. comments inside nowiki tags); if full accuracy is absolutely necessary, use [[Module:template parser]] instead). * {"POST"} applies the method used to generate the final page output once all templates have been expanded: it loops over the text, removing any {{code|html|<nowiki><!-- ... --></nowiki>}} pairs until no more are found (e.g. {{code|html|<nowiki><!-<!-- ... -->- ... --></nowiki>}} would be fully removed), but any unclosed {{code|html|<nowiki><!--</nowiki>}} is ignored. This is suitable for handling links embedded in template inputs, where the {"PRE"} method will have already been applied by the native parser. * {"BOTH"} applies {"PRE"} then {"POST"}.]==] return function(str, stage) if stage == nil or stage == "PRE" then return remove_comments(str, true) elseif stage == "BOTH" then str = remove_comments(str, true) elseif stage ~= "POST" then error("bad argument #2 to 'remove_comments' (expected PRE, POST, or BOTH)", 2) end while true do local processed = remove_comments(str) if processed == str then return str end str = processed end end 6q2u94c4jook0g0vegx0uyg6x3jd1ms Bysen:ang-noun 10 8015 54766 2026-09-25T22:17:03Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:ang-headword|show|nouns}}<noinclude>{{documentation}}</noinclude>' 54766 wikitext text/x-wiki {{#invoke:ang-headword|show|nouns}}<noinclude>{{documentation}}</noinclude> o34yz291seqb0o8y1ogrfmuyq3ve1j0 Module:ang-headword 828 8016 54767 2026-09-25T22:17:29Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local pos_functions = {} local lang = require("Module:languages").getByCode("ang") local insert = table.insert local remove = table.remove local function get_plaintext(...) get_plaintext = require("Module:utilities").get_plaintext return get_plaintext(...) end local function track(page) require("Module:debug").track("ang-headword/" .. page) return true end local function glossary_link(anchor, text) text = text or anchor return "[[Appe...' 54767 Scribunto text/plain local export = {} local pos_functions = {} local lang = require("Module:languages").getByCode("ang") local insert = table.insert local remove = table.remove local function get_plaintext(...) get_plaintext = require("Module:utilities").get_plaintext return get_plaintext(...) end local function track(page) require("Module:debug").track("ang-headword/" .. page) return true end local function glossary_link(anchor, text) text = text or anchor return "[[Appendix:Glossary#" .. anchor .. "|" .. text .. "]]" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local NAMESPACE = mw.title.getCurrentTitle().nsText -- unused -- local PAGENAME = mw.loadData("Module:headword/data").pagename local iparams = { [1] = {required = true}, ["def"] = true, ["suff_type"] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local args = frame:getParent().args local poscat = iargs[1] local def = iargs.def local suff_type = iargs.suff_type local postype = nil if suff_type then postype = poscat .. "-" .. suff_type else postype = poscat end local data = { lang = lang, heads = {}, genders = {}, inflections = {}, categories = {}, no_redundant_head_cat = true, -- we want the headword specified to specify whether it has long vowels } if poscat == "suffixes" then insert(data.categories, "Old English " .. suff_type .. "-forming suffixes") end if pos_functions[postype] then local new_poscat = pos_functions[postype](def, args, data) if new_poscat then poscat = new_poscat end end data.pos_category = poscat return require("Module:headword").full_headword(data) end local function validate_genders(genders) for _, gspec in ipairs(genders) do local g = gspec.spec if g ~= "m" and g ~= "f" and g ~= "n" and g ~= "m-p" and g ~= "f-p" and g ~= "n-p" and g ~= "?" then error("Invalid gender: " .. g) end end end pos_functions["nouns"] = function(def, args, data) local params = { ["head"] = {list = true}, ["id"] = true, [1] = {type = "genders", list = "g", flatten = true, disallow_holes = true}, ["g"] = {alias_of = 1, list = false}, [2] = {list = "pl", disallow_holes = true}, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id validate_genders(args[1]) data.genders = args[1] local pl = args[2] if pl[1] then pl.label = "nominative plural" insert(data.inflections, pl) end end pos_functions["proper nouns"] = pos_functions["nouns"] pos_functions["verbs"] = function(def, args, data) local params = { [1] = {alias_of = "head", list = false}, ["head"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id end local function adjectives(pos, def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["comp"] = list, [2] = {alias_of = "comp", list = false}, ["sup"] = list, [3] = {alias_of = "sup", list = false}, ["adv"] = list, ["indecl"] = {type = "boolean"}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local comp = args.comp if #comp > 0 then comp.label = "comparative" insert(data.inflections, comp) end local sup = args.sup if #sup > 0 then sup.label = "superlative" insert(data.inflections, sup) end if #args.adv > 0 then args.adv.label = "adverb" insert(data.inflections, args.adv) end end pos_functions["adjectives"] = function(def, args, data) return adjectives("adjectives", def, args, data) end pos_functions["participles"] = function(def, args, data) return adjectives("participles", def, args, data) end pos_functions["determiners"] = function(def, args, data) return adjectives("determiners", def, args, data) end pos_functions["pronouns"] = function(def, args, data) return adjectives("pronouns", def, args, data) end pos_functions["suffixes-adjective"] = function(def, args, data) return adjectives("suffixes", def, args, data) end pos_functions["numerals-adjective"] = function(def, args, data) return adjectives("numerals", def, args, data) end pos_functions["adverbs"] = function(def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, [2] = {alias_of = "comp", list = false}, [3] = {alias_of = "sup", list = false}, ["head"] = list, ["comp"] = list, ["sup"] = list, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id local comp, sup if args.comp[1] == "-" then comp = "-" elseif #args.comp > 0 then args.comp.label = glossary_link("comparative") comp = args.comp end if args.comp[1] == "-" or args.sup[1] == "-" then sup = "-" elseif #args.sup > 0 then args.sup.label = glossary_link("superlative") sup = args.sup end if comp == "-" then insert(data.inflections, {label = "not " .. glossary_link("comparable")}) insert(data.categories, "Old English uncomparable adverbs") else insert(data.inflections, comp) end if sup == "-" then if comp ~= "-" then insert(data.inflections, {label = "no " .. glossary_link("superlative")}) end else insert(data.inflections, sup) end end pos_functions["suffixes-adverb"] = pos_functions["adverbs"] local function get_forms(forms) if #forms == 0 then return nil end local i, attested = 1, false while true do local form = forms[i] if form == nil then return forms, attested elseif form == "-" then remove(forms, i) else if not (attested or get_plaintext(form):sub(1, 1) == "*") then attested = true end i = i + 1 end end end local function degree(pos, deg, other_arg, other_label, args, data) local boolean = {type = "boolean"} local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["positive"] = list, [other_arg] = list, ["indecl"] = boolean, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local positive, positive_attested = get_forms(args.positive) if positive then if not positive_attested then insert(data.categories, "Old English " .. deg .. "-only " .. pos) end if #positive > 0 then args.positive.label = "positive" insert(data.inflections, args.positive) else insert(data.inflections, {label = "no positive form"}) end end local other = get_forms(args[other_arg]) if other then if #other > 0 then args[other_arg].label = other_label insert(data.inflections, args[other_arg]) else insert(data.inflections, {label = "no " .. other_label .. " form"}) end end -- If a lemma, return the primary part of speech ("adjectives" or -- "adverbs"), so that the term is categorized in "Old English adjectives" -- or "Old English adverbs". Otherwise, return nothing, so that the term -- goes in the relevant non-lemma category (e.g. "Old English comparative -- adjectives"), and into "Old English non-lemma forms". if positive and not positive_attested then return pos end end pos_functions["comparative adjectives"] = function(def, args, data) return degree("adjectives", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adjectives"] = function(def, args, data) return degree("adjectives", "superlative", "comp", "comparative", args, data) end pos_functions["comparative adverbs"] = function(def, args, data) return degree("adverbs", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adverbs"] = function(def, args, data) return degree("adverbs", "superlative", "comp", "comparative", args, data) end local function non_lemma_forms(def, args, data) local params = { [1] = {required = true, default = def}, -- headword or cases ["head"] = {list = true, require_index = true}, ["g"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) local heads = {args[1]} for _, head in ipairs(args.head) do insert(heads, head) end data.heads = heads data.genders = args.g data.id = args.id end pos_functions["noun forms"] = non_lemma_forms pos_functions["proper noun forms"] = non_lemma_forms pos_functions["pronoun forms"] = non_lemma_forms pos_functions["verb forms"] = non_lemma_forms pos_functions["adjective forms"] = non_lemma_forms pos_functions["participle forms"] = non_lemma_forms pos_functions["determiner forms"] = non_lemma_forms pos_functions["numeral forms"] = non_lemma_forms pos_functions["suffix forms"] = non_lemma_forms return export 8rm183yhaougjiapgy1o5zen3ojx6oa 54778 54767 2026-09-25T22:37:26Z Deadend0914 7211 54778 Scribunto text/plain local export = {} local pos_functions = {} local lang = require("Module:languages").getByCode("ang") local insert = table.insert local remove = table.remove local function get_plaintext(...) get_plaintext = require("Module:utilities").get_plaintext return get_plaintext(...) end local function track(page) require("Module:debug").track("ang-headword/" .. page) return true end local function glossary_link(anchor, text) text = text or anchor return "[[Appendix:Glossary#" .. anchor .. "|" .. text .. "]]" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local NAMESPACE = mw.title.getCurrentTitle().nsText -- unused -- local PAGENAME = mw.loadData("Module:headword/data").pagename local iparams = { [1] = {required = true}, ["def"] = true, ["suff_type"] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local args = frame:getParent().args local poscat = iargs[1] local def = iargs.def local suff_type = iargs.suff_type local postype = nil if suff_type then postype = poscat .. "-" .. suff_type else postype = poscat end local data = { lang = lang, heads = {}, genders = {}, inflections = {}, categories = {}, no_redundant_head_cat = true, -- we want the headword specified to specify whether it has long vowels } if poscat == "suffixes" then insert(data.categories, "Old English " .. suff_type .. "-forming suffixes") end if pos_functions[postype] then local new_poscat = pos_functions[postype](def, args, data) if new_poscat then poscat = new_poscat end end data.pos_category = poscat return require("Module:headword").full_headword(data) end local function validate_genders(genders) for _, gspec in ipairs(genders) do local g = gspec.spec if g ~= "wer" and g ~= "wif" and g ~= "n" and g ~= "m-p" and g ~= "f-p" and g ~= "n-p" and g ~= "?" then error("Invalid gender: " .. g) end end end pos_functions["nouns"] = function(def, args, data) local params = { ["head"] = {list = true}, ["id"] = true, [1] = {type = "genders", list = "g", flatten = true, disallow_holes = true}, ["g"] = {alias_of = 1, list = false}, [2] = {list = "pl", disallow_holes = true}, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id validate_genders(args[1]) data.genders = args[1] local pl = args[2] if pl[1] then pl.label = "nominative plural" insert(data.inflections, pl) end end pos_functions["proper nouns"] = pos_functions["nouns"] pos_functions["verbs"] = function(def, args, data) local params = { [1] = {alias_of = "head", list = false}, ["head"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id end local function adjectives(pos, def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["comp"] = list, [2] = {alias_of = "comp", list = false}, ["sup"] = list, [3] = {alias_of = "sup", list = false}, ["adv"] = list, ["indecl"] = {type = "boolean"}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local comp = args.comp if #comp > 0 then comp.label = "comparative" insert(data.inflections, comp) end local sup = args.sup if #sup > 0 then sup.label = "superlative" insert(data.inflections, sup) end if #args.adv > 0 then args.adv.label = "adverb" insert(data.inflections, args.adv) end end pos_functions["adjectives"] = function(def, args, data) return adjectives("adjectives", def, args, data) end pos_functions["participles"] = function(def, args, data) return adjectives("participles", def, args, data) end pos_functions["determiners"] = function(def, args, data) return adjectives("determiners", def, args, data) end pos_functions["pronouns"] = function(def, args, data) return adjectives("pronouns", def, args, data) end pos_functions["suffixes-adjective"] = function(def, args, data) return adjectives("suffixes", def, args, data) end pos_functions["numerals-adjective"] = function(def, args, data) return adjectives("numerals", def, args, data) end pos_functions["adverbs"] = function(def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, [2] = {alias_of = "comp", list = false}, [3] = {alias_of = "sup", list = false}, ["head"] = list, ["comp"] = list, ["sup"] = list, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id local comp, sup if args.comp[1] == "-" then comp = "-" elseif #args.comp > 0 then args.comp.label = glossary_link("comparative") comp = args.comp end if args.comp[1] == "-" or args.sup[1] == "-" then sup = "-" elseif #args.sup > 0 then args.sup.label = glossary_link("superlative") sup = args.sup end if comp == "-" then insert(data.inflections, {label = "not " .. glossary_link("comparable")}) insert(data.categories, "Old English uncomparable adverbs") else insert(data.inflections, comp) end if sup == "-" then if comp ~= "-" then insert(data.inflections, {label = "no " .. glossary_link("superlative")}) end else insert(data.inflections, sup) end end pos_functions["suffixes-adverb"] = pos_functions["adverbs"] local function get_forms(forms) if #forms == 0 then return nil end local i, attested = 1, false while true do local form = forms[i] if form == nil then return forms, attested elseif form == "-" then remove(forms, i) else if not (attested or get_plaintext(form):sub(1, 1) == "*") then attested = true end i = i + 1 end end end local function degree(pos, deg, other_arg, other_label, args, data) local boolean = {type = "boolean"} local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["positive"] = list, [other_arg] = list, ["indecl"] = boolean, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local positive, positive_attested = get_forms(args.positive) if positive then if not positive_attested then insert(data.categories, "Old English " .. deg .. "-only " .. pos) end if #positive > 0 then args.positive.label = "positive" insert(data.inflections, args.positive) else insert(data.inflections, {label = "no positive form"}) end end local other = get_forms(args[other_arg]) if other then if #other > 0 then args[other_arg].label = other_label insert(data.inflections, args[other_arg]) else insert(data.inflections, {label = "no " .. other_label .. " form"}) end end -- If a lemma, return the primary part of speech ("adjectives" or -- "adverbs"), so that the term is categorized in "Old English adjectives" -- or "Old English adverbs". Otherwise, return nothing, so that the term -- goes in the relevant non-lemma category (e.g. "Old English comparative -- adjectives"), and into "Old English non-lemma forms". if positive and not positive_attested then return pos end end pos_functions["comparative adjectives"] = function(def, args, data) return degree("adjectives", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adjectives"] = function(def, args, data) return degree("adjectives", "superlative", "comp", "comparative", args, data) end pos_functions["comparative adverbs"] = function(def, args, data) return degree("adverbs", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adverbs"] = function(def, args, data) return degree("adverbs", "superlative", "comp", "comparative", args, data) end local function non_lemma_forms(def, args, data) local params = { [1] = {required = true, default = def}, -- headword or cases ["head"] = {list = true, require_index = true}, ["g"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) local heads = {args[1]} for _, head in ipairs(args.head) do insert(heads, head) end data.heads = heads data.genders = args.g data.id = args.id end pos_functions["noun forms"] = non_lemma_forms pos_functions["proper noun forms"] = non_lemma_forms pos_functions["pronoun forms"] = non_lemma_forms pos_functions["verb forms"] = non_lemma_forms pos_functions["adjective forms"] = non_lemma_forms pos_functions["participle forms"] = non_lemma_forms pos_functions["determiner forms"] = non_lemma_forms pos_functions["numeral forms"] = non_lemma_forms pos_functions["suffix forms"] = non_lemma_forms return export 1h5qylfusegcl3qyknq2w33oa9ewqu1 54785 54778 2026-09-25T22:47:51Z Deadend0914 7211 54785 Scribunto text/plain local export = {} local pos_functions = {} local lang = require("Module:languages").getByCode("ang") local insert = table.insert local remove = table.remove local function get_plaintext(...) get_plaintext = require("Module:utilities").get_plaintext return get_plaintext(...) end local function track(page) require("Module:debug").track("ang-headword/" .. page) return true end local function glossary_link(anchor, text) text = text or anchor return "[[Appendix:Glossary#" .. anchor .. "|" .. text .. "]]" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local NAMESPACE = mw.title.getCurrentTitle().nsText -- unused -- local PAGENAME = mw.loadData("Module:headword/data").pagename local iparams = { [1] = {required = true}, ["def"] = true, ["suff_type"] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local args = frame:getParent().args local poscat = iargs[1] local def = iargs.def local suff_type = iargs.suff_type local postype = nil if suff_type then postype = poscat .. "-" .. suff_type else postype = poscat end local data = { lang = lang, heads = {}, genders = {}, inflections = {}, categories = {}, no_redundant_head_cat = true, -- we want the headword specified to specify whether it has long vowels } if poscat == "suffixes" then insert(data.categories, "Englisc " .. suff_type .. "-forming suffixes") end if pos_functions[postype] then local new_poscat = pos_functions[postype](def, args, data) if new_poscat then poscat = new_poscat end end data.pos_category = poscat return require("Module:headword").full_headword(data) end local function validate_genders(genders) for _, gspec in ipairs(genders) do local g = gspec.spec if g ~= "wer" and g ~= "wif" and g ~= "n" and g ~= "m-p" and g ~= "f-p" and g ~= "n-p" and g ~= "?" then error("Invalid gender: " .. g) end end end pos_functions["nouns"] = function(def, args, data) local params = { ["head"] = {list = true}, ["id"] = true, [1] = {type = "genders", list = "g", flatten = true, disallow_holes = true}, ["g"] = {alias_of = 1, list = false}, [2] = {list = "pl", disallow_holes = true}, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id validate_genders(args[1]) data.genders = args[1] local pl = args[2] if pl[1] then pl.label = "nominative plural" insert(data.inflections, pl) end end pos_functions["proper nouns"] = pos_functions["nouns"] pos_functions["verbs"] = function(def, args, data) local params = { [1] = {alias_of = "head", list = false}, ["head"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id end local function adjectives(pos, def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["comp"] = list, [2] = {alias_of = "comp", list = false}, ["sup"] = list, [3] = {alias_of = "sup", list = false}, ["adv"] = list, ["indecl"] = {type = "boolean"}, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local comp = args.comp if #comp > 0 then comp.label = "comparative" insert(data.inflections, comp) end local sup = args.sup if #sup > 0 then sup.label = "superlative" insert(data.inflections, sup) end if #args.adv > 0 then args.adv.label = "adverb" insert(data.inflections, args.adv) end end pos_functions["adjectives"] = function(def, args, data) return adjectives("adjectives", def, args, data) end pos_functions["participles"] = function(def, args, data) return adjectives("participles", def, args, data) end pos_functions["determiners"] = function(def, args, data) return adjectives("determiners", def, args, data) end pos_functions["pronouns"] = function(def, args, data) return adjectives("pronouns", def, args, data) end pos_functions["suffixes-adjective"] = function(def, args, data) return adjectives("suffixes", def, args, data) end pos_functions["numerals-adjective"] = function(def, args, data) return adjectives("numerals", def, args, data) end pos_functions["adverbs"] = function(def, args, data) local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, [2] = {alias_of = "comp", list = false}, [3] = {alias_of = "sup", list = false}, ["head"] = list, ["comp"] = list, ["sup"] = list, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id local comp, sup if args.comp[1] == "-" then comp = "-" elseif #args.comp > 0 then args.comp.label = glossary_link("comparative") comp = args.comp end if args.comp[1] == "-" or args.sup[1] == "-" then sup = "-" elseif #args.sup > 0 then args.sup.label = glossary_link("superlative") sup = args.sup end if comp == "-" then insert(data.inflections, {label = "not " .. glossary_link("comparable")}) insert(data.categories, "Englisc uncomparable adverbs") else insert(data.inflections, comp) end if sup == "-" then if comp ~= "-" then insert(data.inflections, {label = "no " .. glossary_link("superlative")}) end else insert(data.inflections, sup) end end pos_functions["suffixes-adverb"] = pos_functions["adverbs"] local function get_forms(forms) if #forms == 0 then return nil end local i, attested = 1, false while true do local form = forms[i] if form == nil then return forms, attested elseif form == "-" then remove(forms, i) else if not (attested or get_plaintext(form):sub(1, 1) == "*") then attested = true end i = i + 1 end end end local function degree(pos, deg, other_arg, other_label, args, data) local boolean = {type = "boolean"} local list = {list = true} local params = { [1] = {alias_of = "head", list = false}, ["head"] = list, ["positive"] = list, [other_arg] = list, ["indecl"] = boolean, ["id"] = true, } local args = require("Module:parameters").process(args, params) data.heads = args.head data.id = args.id if args.indecl then insert(data.inflections, {label = glossary_link("indeclinable")}) end local positive, positive_attested = get_forms(args.positive) if positive then if not positive_attested then insert(data.categories, "Englisc " .. deg .. "-only " .. pos) end if #positive > 0 then args.positive.label = "positive" insert(data.inflections, args.positive) else insert(data.inflections, {label = "no positive form"}) end end local other = get_forms(args[other_arg]) if other then if #other > 0 then args[other_arg].label = other_label insert(data.inflections, args[other_arg]) else insert(data.inflections, {label = "no " .. other_label .. " form"}) end end -- If a lemma, return the primary part of speech ("adjectives" or -- "adverbs"), so that the term is categorized in "Old English adjectives" -- or "Old English adverbs". Otherwise, return nothing, so that the term -- goes in the relevant non-lemma category (e.g. "Old English comparative -- adjectives"), and into "Old English non-lemma forms". if positive and not positive_attested then return pos end end pos_functions["comparative adjectives"] = function(def, args, data) return degree("adjectives", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adjectives"] = function(def, args, data) return degree("adjectives", "superlative", "comp", "comparative", args, data) end pos_functions["comparative adverbs"] = function(def, args, data) return degree("adverbs", "comparative", "sup", "superlative", args, data) end pos_functions["superlative adverbs"] = function(def, args, data) return degree("adverbs", "superlative", "comp", "comparative", args, data) end local function non_lemma_forms(def, args, data) local params = { [1] = {required = true, default = def}, -- headword or cases ["head"] = {list = true, require_index = true}, ["g"] = {list = true}, ["id"] = true, } local args = require("Module:parameters").process(args, params) local heads = {args[1]} for _, head in ipairs(args.head) do insert(heads, head) end data.heads = heads data.genders = args.g data.id = args.id end pos_functions["noun forms"] = non_lemma_forms pos_functions["proper noun forms"] = non_lemma_forms pos_functions["pronoun forms"] = non_lemma_forms pos_functions["verb forms"] = non_lemma_forms pos_functions["adjective forms"] = non_lemma_forms pos_functions["participle forms"] = non_lemma_forms pos_functions["determiner forms"] = non_lemma_forms pos_functions["numeral forms"] = non_lemma_forms pos_functions["suffix forms"] = non_lemma_forms return export 5uyd4ftha3au3mq91voesix11d9m3dy Module:parameters/track 828 8017 54768 2026-09-25T22:18:03Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local debug_track_module = "Module:debug/track" local string_gline_module = "Module:string/gline" local match = string.match local new_title = mw.title.new local require = require local traceback = debug.traceback local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function gline(...) gline = require(string_gline_module) return gline(...) end local params_title local function get_params_title() para...' 54768 Scribunto text/plain local debug_track_module = "Module:debug/track" local string_gline_module = "Module:string/gline" local match = string.match local new_title = mw.title.new local require = require local traceback = debug.traceback local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function gline(...) gline = require(string_gline_module) return gline(...) end local params_title local function get_params_title() params_title, get_params_title = new_title("parameters", 828), nil return params_title end return function(page, param_name) debug_track("parameters/" .. page) if param_name ~= nil then debug_track("parameters/" .. page .. "/" .. param_name .. "=") end -- Check through the traceback to get the calling module and function. local mod_title for line in gline((traceback())) do local mod, func = match(line, "^\t*(.-):%d+: in function (.*)$") -- Must match a conventional module, not a tail call, C function, in- -- built Scribunto file etc. If `mod` matches `mod_title`, it means the -- module has been seen before, so there is nothing else to check. if mod and not (mod_title and mod == mod_title.prefixedText) then mod_title = new_title(mod) if mod_title and mod_title.namespace == 828 and not ( mod_title == (params_title or get_params_title()) or mod_title:isSubpageOf(params_title) ) then mod = mod_title.text debug_track("parameters/" .. page .. "/" .. mod) -- traceback() encloses function names in single quotes. local funcname = match(func, "^'(.*)'$") if funcname then debug_track("parameters/" .. page .. "/" .. mod .. "/" .. funcname) return end -- WHen a function is unnamed, line numbers are given after the -- module name in angle brackets, directly after a separating -- colon. local funcline = match(func, "^<.-:(%d+)>$") if funcline then debug_track("parameters/" .. page .. "/" .. mod .. ":" .. funcline) end return end end end end ep6rfkuslbwvncu36j3fa17iqe4seb6 Module:string/gline 828 8018 54769 2026-09-25T22:18:31Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local error = error local find = string.find local gmatch = string.gmatch local match = string.match local sub = string.sub --[==[ Iterates over the lines in a string, treating {"\n"}, {"\r"} and {"\r\n"} as new lines. The optional {skip} parameter determines whether certain lines are skipped: * {NONE}: none (default). * {EMPTY}: empty lines: lines with a length of 0, consisting of no characters. * {BLANK}: blank lines: empty lines and lines which only consist...' 54769 Scribunto text/plain local error = error local find = string.find local gmatch = string.gmatch local match = string.match local sub = string.sub --[==[ Iterates over the lines in a string, treating {"\n"}, {"\r"} and {"\r\n"} as new lines. The optional {skip} parameter determines whether certain lines are skipped: * {NONE}: none (default). * {EMPTY}: empty lines: lines with a length of 0, consisting of no characters. * {BLANK}: blank lines: empty lines and lines which only consist of whitespace characters.]==] return function(str, skip) -- Use gmatch() for EMPTY and BLANK, for speed. if skip == "EMPTY" then return gmatch(str, "[^\n\r]+") elseif skip == "BLANK" then return gmatch(str, "[^\n\r]-%S[^\n\r]*") elseif not (skip == nil or skip == "NONE") then error("bad argument #2 to 'string/gline' (expected NONE, EMPTY or BLANK)", 2) end local start, linechar, flag = 1 if not find(str, "\n", nil, true) then if not find(str, "\r", nil, true) then -- No newlines: return `str` on the first iteration then end. return function() if flag then return end flag = true return str end end linechar = "\r" elseif find(str, "\r", nil, true) then -- Newline could be "\n", "\r" or "\r\n" (but not "\n\r"). return function() if not flag then local line, nl, pos = match(str, "^([^\n\r]*)([\n\r]\n?)()", start) if not line then flag = true return sub(str, start) end -- If `nl` is "\r\n" it should be treated as a single newline, -- but if it's "\n\n" it needs to be treated as two separate -- newlines, so set `flag` to 2 to avoid an unnecessary match() -- call on the next iteration. if nl == "\n\n" then flag = 2 end start = pos return line elseif flag == 2 then flag = nil return "" end end else linechar = "\n" end -- Newline is either "\n" or "\r", depending on which has been found. return function() if flag then return end local pos = find(str, linechar, start, true) if not pos then flag = true return sub(str, start) end local line = sub(str, start, pos - 1) start = pos + 1 return line end end r6wdku3u8kbsa2pnl1f07kxp328mzdt Module:headword 828 8019 54770 2026-09-25T22:19:00Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} -- Named constants for all modules used, to make it easier to swap out sandbox versions. local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local en_utilities_module = "Module:en-utilities" local gender_and_number_module = "Module:gender and number" local headword_data_module = "Module:headword/data" local headword_page_module = "Module:headword/page" local links_module = "Module:links" local load_mo...' 54770 Scribunto text/plain local export = {} -- Named constants for all modules used, to make it easier to swap out sandbox versions. local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local en_utilities_module = "Module:en-utilities" local gender_and_number_module = "Module:gender and number" local headword_data_module = "Module:headword/data" local headword_page_module = "Module:headword/page" local links_module = "Module:links" local load_module = "Module:load" local pages_module = "Module:pages" local palindromes_module = "Module:palindromes" local scripts_module = "Module:scripts" local scripts_data_module = "Module:scripts/data" local script_utilities_module = "Module:script utilities" local script_utilities_data_module = "Module:script utilities/data" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local dump = mw.dumpObject local insert = table.insert local ipairs = ipairs local max = math.max local new_title = mw.title.new local pairs = pairs local require = require local toNFC = mw.ustring.toNFC local toNFD = mw.ustring.toNFD local type = type local ufind = mw.ustring.find local ugmatch = mw.ustring.gmatch local ugsub = mw.ustring.gsub local umatch = mw.ustring.match --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function contains(...) contains = require(table_module).contains return contains(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function full_link(...) full_link = require(links_module).full_link return full_link(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_link_page(...) get_link_page = require(links_module).get_link_page return get_link_page(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function is_palindrome(...) is_palindrome = require(palindromes_module).is_palindrome return is_palindrome(...) end local function language_link(...) language_link = require(links_module).language_link return language_link(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function pluralize(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) end local function process_page(...) process_page = require(headword_page_module).process_page return process_page(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_transcription(...) tag_transcription = require(script_utilities_module).tag_transcription return tag_transcription(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local m_data local function get_data() m_data = load_data(headword_data_module) return m_data end local script_data local function get_script_data() script_data = load_data(scripts_data_module) return script_data end local script_utilities_data local function get_script_utilities_data() script_utilities_data = load_data(script_utilities_data_module) return script_utilities_data end -- If set to true, categories always appear, even in non-mainspace pages local test_force_categories = false -- Add a tracking category to track entries with certain (unusually undesirable) properties. `track_id` is an identifier -- for the particular property being tracked and goes into the tracking page. Specifically, this adds a link in the -- page text to [[Wiktionary:Tracking/headword/TRACK_ID]], meaning you can find all entries with the `track_id` property -- by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID]]. -- -- If `lang` (a language object) is given, an additional tracking page [[Wiktionary:Tracking/headword/TRACK_ID/CODE]] is -- linked to where CODE is the language code of `lang`, and you can find all entries in the combination of `track_id` -- and `lang` by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID/CODE]]. This makes it possible to -- isolate only the entries with a specific tracking property that are in a given language. Note that if `lang` -- references at etymology-only language, both that language's code and its full parent's code are tracked. local function track(track_id, lang) local tracking_page = "headword/" .. track_id if lang and lang:hasType("etymology-only") then debug_track{tracking_page, tracking_page .. "/" .. lang:getCode(), tracking_page .. "/" .. lang:getFullCode()} elseif lang then debug_track{tracking_page, tracking_page .. "/" .. lang:getCode()} else debug_track(tracking_page) end return true end local function text_in_script(text, script_code) local sc = get_script(script_code) if not sc then error("Internal error: Bad script code " .. script_code) end local characters = sc.characters local out if characters then text = ugsub(text, "%W", "") out = ufind(text, "[" .. characters .. "]") end if out then return true else return false end end local spacingPunctuation = "[%s%p]+" --[[ List of punctuation or spacing characters that are found inside of words. Used to exclude characters from the regex above. ]] local wordPunc = "-#%%&@־׳״'.·*’་•:᠊" local notWordPunc = "[^" .. wordPunc .. "]+" --[=[ Format a term (either a head term or an inflection term) along with any decorations (left or right qualifiers, labels, references or customized separator). `part` is the object specifying the term (and `lang` the language of the term), which should optionally contain: * left qualifiers in `q`, an array of strings; * right qualifiers in `qq`, an array of strings; * left labels in `l`, an array of strings; * right labels in `ll`, an array of strings; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; * a separator in `separator`, defaulting to " <i>or</i> " if this is not the first term (j > 1), otherwise "". `formatted` is the formatted version of the term itself, and `j` is the index of the term. ]=] local function format_term_with_decorations(lang, part, formatted, j) local function part_non_empty(field) local list = part[field] if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `part.%s`=%s, should be \"table\""):format(field, dump(list))) end return list[1] end if part_non_empty("q") or part_non_empty("qq") or part_non_empty("l") or part_non_empty("ll") or part_non_empty("refs") then formatted = format_decorations { lang = lang, text = formatted, q = part.q, qq = part.qq, l = part.l, ll = part.ll, refs = part.refs, } end local separator = part.separator or j > 1 and " <i>or</i> " -- use "" to request no separator if separator then formatted = separator .. formatted end return formatted end --[==[Return true if the given head is multiword according to the algorithm used in full_headword().]==] function export.head_is_multiword(head) for possibleWordBreak in ugmatch(head, spacingPunctuation) do if umatch(possibleWordBreak, notWordPunc) then return true end end return false end do local function workaround_to_exclude_chars(s) return (ugsub(s, notWordPunc, "\2%1\1")) end --[==[ Add appropriate links to `head`, correctly handling multiword terms. This is intended for multiword terms but can be used for any term if you want links added to single-word terms as well. If you want to only add links to multiword terms, first check that the term is multiword using `head_is_multiword`. If `default` is specified, this will escape colons so that they don't get interpreted as interwiki links. This should generally only be used when `head` is an actual pagename or is taken from a {{para|pagename}} parameter, not when taken from a {{para|head}} parameter. ]==] function export.add_multiword_links(head, default) head = "\1" .. ugsub(head, spacingPunctuation, workaround_to_exclude_chars) .. "\2" if default then head = head :gsub("(\1[^\2]*)\\([:#][^\2]*\2)", "%1\\\\%2") :gsub("(\1[^\2]*)([:#][^\2]*\2)", "%1\\%2") end --Escape any remaining square brackets to stop them breaking links (e.g. "[citation needed]"). head = encode_entities(head, "[]", true, true) --[=[ use this when workaround is no longer needed: head = "[[" .. ugsub(head, WORDBREAKCHARS, "]]%1[[") .. "]]" Remove any empty links, which could have been created above at the beginning or end of the string. ]=] return (head :gsub("\1\2", "") :gsub("[\1\2]", {["\1"] = "[[", ["\2"] = "]]"})) end end local function non_categorizable(full_raw_pagename) return full_raw_pagename:find("^Appendix:Gestures/") or -- Unsupported titles with descriptive names. (full_raw_pagename:find("^Unsupported titles/") and not full_raw_pagename:find("`")) end local function tag_text_and_add_decorations(data, head, formatted, j) -- Add language and script wrapper. formatted = tag_text(formatted, data.lang, head.sc, "head", nil, j == 1 and data.id or nil) -- Add decorations (qualifiers, labels, references and separator). return format_term_with_decorations(data.lang, head, formatted, j) end -- Format a headword with transliterations. local function format_headword(data) -- Are there non-empty transliterations? local has_translits = false local has_manual_translits = false ------ Format the headwords. ------ local head_parts = {} local unique_head_parts = {} local has_multiple_heads = not not data.heads[2] for j, head in ipairs(data.heads) do if head.tr or head.ts then has_translits = true end if head.tr and head.tr_manual or head.ts then has_manual_translits = true end local formatted -- Apply processing to the headword, for formatting links and such. if head.term:find("[[", nil, true) and head.sc:getCode() ~= "Image" then formatted = language_link{term = head.term, lang = data.lang} else formatted = data.lang:makeDisplayText(head.term, head.sc, true) end local head_part = tag_text_and_add_decorations(data, head, formatted, j) insert(head_parts, head_part) -- If multiple heads, try to determine whether all heads display the same. To do this we need to effectively -- rerun the text tagging and addition of decorations, using 1 for all indices. if has_multiple_heads then local unique_head_part if j == 1 then unique_head_part = head_part else unique_head_part = tag_text_and_add_decorations(data, head, formatted, 1) end unique_head_parts[unique_head_part] = true end end local set_size = 0 if has_multiple_heads then for _ in pairs(unique_head_parts) do set_size = set_size + 1 end end if set_size == 1 then head_parts = head_parts[1] else head_parts = concat(head_parts) end if has_manual_translits then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr/LANGCODE]] track("manual-tr", data.lang) end ------ Format the transliterations and transcriptions. ------ local translits_formatted if has_translits then local translit_parts = {} for _, head in ipairs(data.heads) do if head.tr or head.ts then local this_parts = {} if head.tr then insert(this_parts, tag_translit(head.tr, data.lang:getCode(), "head", nil, head.tr_manual)) if head.ts then insert(this_parts, " ") end end if head.ts then insert(this_parts, "/" .. tag_transcription(head.ts, data.lang:getCode(), "head") .. "/") end insert(translit_parts, concat(this_parts)) end end translits_formatted = " (" .. concat(translit_parts, " <i>or</i> ") .. ")" local function format_transliteration_page_link(langname) local transliteration_pagename = langname .. " transliteration" local transliteration_page = new_title(transliteration_pagename, "Wiktionary") if transliteration_page and transliteration_page:getContent() then return ("[[Wiktionary:%s|•]]"):format(transliteration_pagename) end return nil end local translit_page_link = format_transliteration_page_link(data.lang:getCanonicalName()) -- If data.lang is an etymology-only language and we didn't find a translation page for it, fall back to the -- full parent. if not translit_page_link and data.lang:hasType("etymology-only") then translit_page_link = format_transliteration_page_link(data.lang:getFullName()) end if translit_page_link then translits_formatted = " " .. translit_page_link .. translits_formatted end else translits_formatted = "" end ------ Paste heads and transliterations/transcriptions. ------ local lemma_gloss if data.gloss then lemma_gloss = ' <span class="ib-content qualifier-content">' .. data.gloss .. '</span>' else lemma_gloss = "" end return head_parts .. translits_formatted .. lemma_gloss end local function format_headword_genders(data, is_varform_only) local retval = "" if data.genders and data.genders[1] then if data.gloss then retval = "," end local pos_for_cat if not data.nogendercat and not is_varform_only then local no_gender_cat = (m_data or get_data()).no_gender_cat if not (no_gender_cat[data.lang:getCode()] or no_gender_cat[data.lang:getFullCode()]) then pos_for_cat = (m_data or get_data()).pos_for_gender_number_cat[data.pos_category:gsub("^reconstructed ", "")] end end local text, cats = format_genders(data.genders, data.lang, pos_for_cat) if cats then extend(data.categories, cats) end retval = retval .. "&nbsp;" .. text end return retval end -- Forward reference local format_inflections local function format_inflection_parts(data, parts) for j, part in ipairs(parts) do if type(part) ~= "table" then part = {term = part} end local partaccel = part.accel local face = part.face or "bold" if face ~= "bold" and face ~= "plain" and face ~= "hypothetical" then error("The face `" .. face .. "` " .. ( (script_utilities_data or get_script_utilities_data()).faces[face] and "should not be used for non-headword terms on the headword line." or "is invalid." )) end -- Here the final part 'or data.nolinkinfl' allows to have 'nolinkinfl=true' -- right into the 'data' table to disable inflection links of the entire headword -- when inflected forms aren't entry-worthy, e.g.: in Vulgar Latin local nolinkinfl = part.face == "hypothetical" or (part.nolink and track("nolink") or part.nolinkinfl) or ( data.nolink and track("nolink") or data.nolinkinfl) local formatted if part.label then -- FIXME: There should be a better way of italicizing a label. As is, this isn't customizable. formatted = "<i>" .. part.label .. "</i>" else -- Convert the term into a full link. Don't show a transliteration here unless enable_auto_translit is -- requested, either at the `parts` level (i.e. per inflection) or at the `data.inflections` level (i.e. -- specified for all inflections). This is controllable in {{head}} using autotrinfl=1 for all inflections, -- or fNautotr=1 for an individual inflection (remember that a single inflection may be associated with -- multiple terms). The reason for doing this is to avoid clutter in headword lines by default in languages -- where the script is relatively straightforward to read by learners (e.g. Greek, Russian), but allow it -- to be enabled in languages with more complex scripts (e.g. Arabic). -- -- FIXME: With nested inflections, should we also respect `enable_auto_translit` at the top level of the -- nested inflections structure? local tr = part.tr or not (parts.enable_auto_translit or data.inflections.enable_auto_translit) and "-" or nil local postprocess_annotations if part.inflections then postprocess_annotations = function(infldata) insert(infldata.annotations, format_inflections(data, part.inflections)) end end formatted = full_link( { term = not nolinkinfl and part.term or nil, alt = part.alt or (nolinkinfl and part.term or nil), lang = part.lang or data.lang, sc = part.sc or parts.sc or nil, gloss = part.gloss, pos = part.pos, lit = part.lit, id = part.id, genders = part.genders, tr = tr, ts = part.ts, accel = partaccel or parts.accel, postprocess_annotations = postprocess_annotations, }, face ) end parts[j] = format_term_with_decorations(part.lang or data.lang, part, formatted, j) end local parts_output if parts[1] then parts_output = (parts.label and " " or "") .. concat(parts) elseif parts.request then parts_output = " <small>[please provide]</small>" insert(data.categories, "Requests for inflections in " .. data.lang:getFullName() .. " entries") else parts_output = "" end local parts_label = parts.label and ("<i>" .. parts.label .. "</i>") or "" return format_term_with_decorations(data.lang, parts, parts_label .. parts_output, 1) end -- Format the inflections following the headword or nested after a given inflection. Declared local above. function format_inflections(data, inflections) if inflections and inflections[1] then -- Format each inflection individually. for key, infl in ipairs(inflections) do inflections[key] = format_inflection_parts(data, infl) end return concat(inflections, ", ") else return "" end end -- Format the top-level inflections following the headword. Currently this just adds parens around the -- formatted comma-separated inflections in `data.inflections`. local function format_top_level_inflections(data) local result = format_inflections(data, data.inflections) if result ~= "" then return " (" .. result .. ")" else return result end end -- Forward reference local check_red_link_inflections -- Check a single inflection (which consists of a label and zero or more terms, each possibly with nested inflections) -- for red links. If so, insert a red-link category based on `plpos` (the plural part of speech to insert in the -- category), stop further processing, and return true. If no red links found, return false. local function check_red_link_inflection_parts(data, parts, plpos) for _, part in ipairs(parts) do if type(part) ~= "table" then part = {term = part} end local term = part.term if term and not term:find("%[%[") then local stripped_physical_term = get_link_page(term, data.lang, part.sc or parts.sc or nil) if stripped_physical_term then local title = mw.title.new(stripped_physical_term) if title and not title:getContent() then insert(data.categories, data.lang:getFullName() .. " " .. plpos .. " with red links in their headword lines") return true end end end if part.inflections then if check_red_link_inflections(data, part.inflections, plpos) then return true end end end return false end -- Check a set of inflections (each of which describes a single inflection of the term, such as feminine or plural, and -- consists of a label and zero or more terms, each possibly with nested inflections) for red links. If so, insert a -- red-link category based on `plpos` (the plural part of speech to insert in the category), stop further processing, -- and return true. If no red links found, return false. function check_red_link_inflections(data, inflections, plpos) if inflections and inflections[1] then -- Check each inflection individually. for key, infl in ipairs(inflections) do if check_red_link_inflection_parts(data, infl, plpos) then return true end end end return false end -- Check the top-level inflections in `data.inflections`, along with any nested inflections, for red links. If so, -- insert a red-link category based on `plpos` (the plural part of speech to insert in the category), stop further -- processing, and return true. If no red links found, return false. local function check_red_link_inflections_top_level(data, plpos) return check_red_link_inflections(data, data.inflections, plpos) end --[==[ Returns the plural form of `pos`, a raw part of speech input, which could be singular or plural. Irregular plural POS are taken into account (e.g. "kanji" pluralizes to "kanji"). ]==] function export.pluralize_pos(pos) -- Make the plural form of the part of speech return (m_data or get_data()).irregular_plurals[pos] or pos:sub(-1) == "s" and pos or pluralize(pos) end --[==[ Return "lemma" if the given POS is a lemma, "non-lemma form" if a non-lemma form, or nil if unknown. The POS passed in must be in its plural form ("nouns", "prefixes", etc.). If you have a POS in its singular form, call {export.pluralize_pos()} above to pluralize it in a smart fashion that knows when to add "-s" and when to add "-es", and also takes into account any irregular plurals. If `best_guess` is given and the POS is in neither the lemma nor non-lemma list, guess based on whether it ends in " forms"; otherwise, return nil. ]==] function export.pos_lemma_or_nonlemma(plpos, best_guess) local m_headword_data = m_data or get_data() local isLemma = m_headword_data.lemmas -- Is it a lemma category? if isLemma[plpos] then return "lemma" end local plpos_no_recon = plpos:gsub("^reconstructed ", "") if isLemma[plpos_no_recon] then return "lemma" end -- Is it a nonlemma category? local isNonLemma = m_headword_data.nonlemmas if isNonLemma[plpos] or isNonLemma[plpos_no_recon] then return "non-lemma form" end local plpos_no_mut = plpos:gsub("^mutated ", "") if isLemma[plpos_no_mut] or isNonLemma[plpos_no_mut] then return "non-lemma form" elseif best_guess then return plpos:find(" forms$") and "non-lemma form" or "lemma" else return nil end end --[==[ Canonicalize a part of speech as specified in 2= in {{tl|head}}. This checks for POS aliases and non-lemma form aliases ending in 'f', and then pluralizes if the POS term does not have an invariable plural. ]==] function export.canonicalize_pos(pos) -- FIXME: Temporary code to throw an error for alias 'pre' (= preposition) that will go away. if pos == "pre" then -- Don't throw error on 'pref' as it's an alias for "prefix". error("POS 'pre' for 'preposition' no longer allowed as it's too ambiguous; use 'prep'") end -- Likewise for pro = pronoun. if pos == "pro" or pos == "prof" then error("POS 'pro' for 'pronoun' no longer allowed as it's too ambiguous; use 'pron'") end local m_headword_data = m_data or get_data() if m_headword_data.pos_aliases[pos] then pos = m_headword_data.pos_aliases[pos] elseif pos:sub(-1) == "f" then pos = pos:sub(1, -2) pos = (m_headword_data.pos_aliases[pos] or pos) .. " forms" end return export.pluralize_pos(pos) end -- Find and return the maximum index in the array `data[element]` (which may have gaps in it), and initialize it to a -- zero-length array if unspecified. Check to make sure all keys are numeric (other than "maxindex", which is set by -- [[Module:parameters]] for list parameters), all values are strings, and unless `allow_blank_string` is given, -- no blank (zero-length) strings are present. local function init_and_find_maximum_index(data, element, allow_blank_string) local maxind = 0 if not data[element] then data[element] = {} end local typ = type(data[element]) if typ ~= "table" then error(("Internal error: In full_headword(), `data.%s` must be an array but is a %s"):format(element, typ)) end for k, v in pairs(data[element]) do if k ~= "maxindex" then if type(k) ~= "number" then error(("Internal error: Unrecognized non-numeric key '%s' in `data.%s`"):format(k, element)) end if k > maxind then maxind = k end if v then if type(v) ~= "string" then error(("Internal error: For key '%s' in `data.%s`, value should be a string but is a %s"):format(k, element, type(v))) end if not allow_blank_string and v == "" then error(("Internal error: For key '%s' in `data.%s`, blank string not allowed; use 'false' for the default"):format(k, element)) end end end end return maxind end --[==[ -- Add the page to various maintenance categories for the language and the -- whole page. These are placed in the headword somewhat arbitrarily, but -- mainly because headword templates are mandatory for entries (meaning that -- in theory it provides full coverage). -- -- This is provided as an external entry point so that modules which transclude -- information from other entries (such as {{tl|ja-see}}) can take advantage -- of this feature as well, because they are used in place of a conventional -- headword template.]==] do -- Handle any manual sortkeys that have been specified in raw categories -- by tracking if they are the same or different from the automatically- -- generated sortkey, so that we can track them in maintenance -- categories. local function handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) sortkey = sortkey or lang:makeSortKey(page.pagename) -- If there are raw categories with no sortkey, then they will be -- sorted based on the default MediaWiki sortkey, so we check against -- that. if tbl == true then if page.raw_defaultsort ~= sortkey then insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys") end return end local redundant, different for k in pairs(tbl) do if k == sortkey then redundant = true else different = true end end if redundant then insert(lang_cats, lang:getFullName() .. " terms with redundant sortkeys") end if different then insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys") end return sortkey end function export.maintenance_cats(page, lang, lang_cats, page_cats) extend(page_cats, page.cats) lang = lang:getFull() -- since we are just generating categories local canonical = lang:getCanonicalName() local tbl = page.wikitext_topic_cat[lang:getCode()] local sortkey = nil if tbl then sortkey = handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) insert(lang_cats, canonical .. " entries with topic categories using raw markup") end tbl = page.wikitext_langname_cat[canonical] if tbl then handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) insert(lang_cats, canonical .. " entries with language name categories using raw markup") end if get_current_L2() ~= canonical then insert(lang_cats, canonical .. " entries with incorrect language header") -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header/LANGCODE]] track("incorrect language header", lang) end end end --[==[This is the primary external entry point. {{lua|full_headword(data)}} This is used by {{temp|head}} and various language-specific headword templates (e.g. {{temp|ru-adj}} for Russian adjectives, {{temp|de-noun}} for German nouns, etc.) to display an entire headword line. See [[#Further explanations for full_headword()]] ]==] function export.full_headword(data) -- Prevent data from being destructively modified. data = shallow_copy(data) ------------ 1. Basic checks for old-style (multi-arg) calling convention. ------------ if data.getCanonicalName then error("Internal error: In full_headword(), the first argument `data` needs to be a Lua object (table) of properties, not a language object") end if not data.lang or type(data.lang) ~= "table" or not data.lang.getCode then error("Internal error: In full_headword(), the first argument `data` needs to be a Lua object (table) and `data.lang` must be a language object") end if data.id and type(data.id) ~= "string" then error("Internal error: The id in the data table should be a string.") end ------------ 2. Initialize pagename etc. ------------ local langcode = data.lang:getCode() local full_langcode = data.lang:getFullCode() local langname = data.lang:getCanonicalName() local full_langname = data.lang:getFullName() local raw_pagename = data.pagename local page local m_headword_data = m_data or get_data() if raw_pagename and raw_pagename ~= m_headword_data.pagename then -- for testing, doc pages, etc. -- data.pagename is often set on documentation and test pages through the pagename= parameter of various -- templates, to emulate running on that page. Having a large number of such test templates on a single -- page often leads to timeouts, because we fetch and parse the contents of each page in turn. However, -- we don't really need to do that and can function fine without fetching and parsing the contents of a -- given page, so turn off content fetching/parsing (and also setting the DEFAULTSORT key through a parser -- function, which is *slooooow*) in certain namespaces where test and documentation templates are likely to -- be found and where actual content does not live (User, Template, Module). local actual_namespace = m_headword_data.page.namespace local no_fetch_content = actual_namespace == "User" or actual_namespace == "Template" or actual_namespace == "Module" page = process_page(raw_pagename, no_fetch_content) else page = m_headword_data.page end local namespace = page.namespace if data.altform then -- Temporary tracking for use of old altform= track("altform", data.lang) end local noposcat = data.noposcat local is_varform_only = data.var and data.var ~= "both" local is_varform_both = data.var == "both" ------------ 3. Initialize `data.heads` table; if old-style, convert to new-style. ------------ if type(data.heads) == "table" and type(data.heads[1]) == "table" then -- new-style if data.translits or data.transcriptions then error("Internal error: In full_headword(), if `data.heads` is new-style (array of head objects), `data.translits` and `data.transcriptions` cannot be given") end else -- convert old-style `heads`, `translits` and `transcriptions` to new-style local maxind = max( init_and_find_maximum_index(data, "heads"), init_and_find_maximum_index(data, "translits", true), init_and_find_maximum_index(data, "transcriptions", true) ) for i = 1, maxind do data.heads[i] = { term = data.heads[i], tr = data.translits[i], ts = data.transcriptions[i], } end end -- Make sure there's at least one head. if not data.heads[1] then data.heads[1] = {} end ------------ 4. Initialize and validate `data.categories` and `data.whole_page_categories`, and determine `pos_category` if not given, and add basic categories. ------------ init_and_find_maximum_index(data, "categories") init_and_find_maximum_index(data, "whole_page_categories") local pos_category_already_present = false if data.categories[1] then local escaped_langname = pattern_escape(full_langname) local matches_lang_pattern = "^" .. escaped_langname .. " " for _, cat in ipairs(data.categories) do -- Does the category begin with the language name? If not, tag it with a tracking category. if not cat:find(matches_lang_pattern) then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category/LANGCODE]] track("no lang category", data.lang) end end -- If `pos_category` not given, try to infer it from the first specified category. If this doesn't work, we -- throw an error below. if not data.pos_category and data.categories[1]:find(matches_lang_pattern) then data.pos_category = data.categories[1]:gsub(matches_lang_pattern, "") -- Optimization to avoid inserting category already present. pos_category_already_present = true end end if not data.pos_category then error("Internal error: `data.pos_category` not specified and could not be inferred from the categories given in " .. "`data.categories`. Either specify the plural part of speech in `data.pos_category` " .. "(e.g. \"proper nouns\") or ensure that the first category in `data.categories` is formed from the " .. "language's canonical name plus the plural part of speech (e.g. \"Norwegian Bokmål proper nouns\")." ) end -- Insert a category at the beginning for the part of speech unless it's already present or `data.noposcat` given. if not (pos_category_already_present or noposcat or is_varform_only) then local pos_category = full_langname .. " " .. data.pos_category -- FIXME: [[User:Theknightwho]] Why is this special case here? Please add an explanatory comment. if pos_category ~= "Translingual Han characters" then insert(data.categories, 1, pos_category) end end -- Try to determine whether the part of speech refers to a lemma or a non-lemma form; if we can figure this out, -- add an appropriate category. local postype = export.pos_lemma_or_nonlemma(data.pos_category) if not postype then -- We don't know what this category is, so tag it with a tracking category. -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANGCODE]] track("unrecognized pos", data.lang) -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS/LANGCODE]] track("unrecognized pos/pos/" .. data.pos_category, data.lang) elseif not (noposcat or is_varform_only or data.nolemmacat) then insert(data.categories, 1, full_langname .. " " .. postype .. "s") end -- Categorize variant forms into 'variant lemmas' or 'variant non-lemma forms'. Originally proposed in -- [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] as 'alternative forms'; renamed in -- [[Wiktionary:Beer parlour/2026/July#Renaming the "alternative forms" categories]]. if (is_varform_only or is_varform_both) and postype then insert(data.categories, 1, full_langname .. " variant " .. postype .. "s") end ------------ 5. Create a default headword, and add links to multiword page names. ------------ -- Determine if this is an "anti-asterisk" term, i.e. an attested term in a language that must normally be -- reconstructed. local is_anti_asterisk = data.heads[1].term and data.heads[1].term:find("^!!") local lang_reconstructed = data.lang:hasType("reconstructed") if is_anti_asterisk then if not lang_reconstructed then error("Anti-asterisk feature (head= beginning with !!) can only be used with reconstructed languages") end if namespace == "Reconstruction" then error("Anti-asterisk feature (head= beginning with !!) should be used in mainspace, not in Reconstruction space") end lang_reconstructed = false end -- Determine if term is reconstructed local is_reconstructed = namespace == "Reconstruction" or lang_reconstructed -- Create a default headword based on the pagename, which is determined in -- advance by the data module so that it only needs to be done once. local default_head = page.pagename -- Add links to multi-word page names when appropriate if not (is_reconstructed or data.nolinkhead) then local no_links = m_headword_data.no_multiword_links if not (no_links[langcode] or no_links[full_langcode]) and export.head_is_multiword(default_head) then default_head = export.add_multiword_links(default_head, true) end end if is_reconstructed then default_head = "*" .. default_head end ------------ 6. Check the namespace against the language type. ------------ if namespace == "" then if lang_reconstructed then error("Entries in " .. langname .. " must be placed in the Reconstruction: namespace") elseif data.lang:hasType("appendix-constructed") then error("Entries in " .. langname .. " must be placed in the Appendix: namespace") end elseif namespace == "Citations" or namespace == "Thesaurus" then error("Headword templates should not be used in the " .. namespace .. ": namespace.") end ------------ 7. Fill in missing values in `data.heads`. ------------ -- True if any script among the headword scripts has spaces in it. local any_script_has_spaces = false -- True if any term has a redundant head= param. local has_redundant_head_param = false for _, head in ipairs(data.heads) do ------ 7a. If missing head, replace with default head. if not head.term then head.term = default_head elseif head.term == default_head then has_redundant_head_param = true elseif is_anti_asterisk and head.term == "!!" then -- If explicit head=!! is given, it's an anti-asterisk term and we fill in the default head. head.term = "!!" .. default_head elseif head.term:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. head.term = default_head .. head.term end head.term_no_initial_bang_bang = is_anti_asterisk and head.term:sub(3) or head.term if is_reconstructed then local head_term = head.term if head_term:find("%[%[") then head_term = remove_links(head_term) end if head_term:sub(1, 1) ~= "*" then error("The headword '" .. head_term .. "' must begin with '*' to indicate that it is reconstructed.") end end ------ 7b. Try to detect the script(s) if not provided. If a per-head script is provided, that takes precedence, ------ otherwise fall back to the overall script if given. If neither given, autodetect the script. local auto_sc = data.lang:findBestScript(head.term) if ( auto_sc:getCode() == "None" and find_best_script_without_lang(head.term):getCode() ~= "None" ) then insert(data.categories, full_langname .. " terms in nonstandard scripts") end if not (head.sc or data.sc) then -- No script code given, so use autodetected script. head.sc = auto_sc else if not head.sc then -- Overall script code given. head.sc = data.sc end -- Track uses of sc parameter. if head.sc:getCode() == auto_sc:getCode() then track("redundant script code", data.lang) if not data.no_script_code_cat then insert(data.categories, full_langname .. " terms with redundant script codes") end else track("non-redundant manual script code", data.lang) if not data.no_script_code_cat then insert(data.categories, full_langname .. " terms with non-redundant manual script codes") end end end -- If using a discouraged character sequence, add to maintenance category. if head.sc:hasNormalizationFixes() == true then local composed_head = toNFC(head.term) if head.sc:fixDiscouragedSequences(composed_head) ~= composed_head then insert(data.whole_page_categories, "Pages using discouraged character sequences") end end any_script_has_spaces = any_script_has_spaces or head.sc:hasSpaces() ------ 7c. Create automatic transliterations for any non-Latin headwords without manual translit given ------ (provided automatic translit is available, e.g. not in Persian or Hebrew). -- Make transliterations head.tr_manual = nil -- Try to generate a transliteration if necessary if head.tr == "-" then head.tr = nil else local notranslit = m_headword_data.notranslit if not (notranslit[langcode] or notranslit[full_langcode]) and head.sc:isTransliterated() then head.tr_manual = not not head.tr local text = head.term_no_initial_bang_bang if not data.lang:link_tr(head.sc) then text = remove_links(text) end local automated_tr = data.lang:transliterate(text, head.sc) if automated_tr then local manual_tr = head.tr if manual_tr then if remove_links(manual_tr) == remove_links(automated_tr) then insert(data.categories, full_langname .. " terms with redundant transliterations") else insert(data.categories, full_langname .. " terms with non-redundant manual transliterations") end end if not manual_tr then head.tr = automated_tr end end -- There is still no transliteration? -- Add the entry to a cleanup category. if not head.tr then head.tr = "<small>transliteration needed</small>" -- FIXME: No current support for 'Request for transliteration of Classical Persian terms' or similar. -- Consider adding this support in [[Module:category tree/poscatboiler/data/entry maintenance]]. insert(data.categories, "Requests for transliteration of " .. full_langname .. " terms") else -- Otherwise, trim it. head.tr = trim(head.tr) end end end -- Link to the transliteration entry for languages that require this. if head.tr and data.lang:link_tr(head.sc) then head.tr = full_link{ term = head.tr, lang = data.lang, sc = get_script("Latn"), tr = "-" } end end ------------ 8. Maybe tag the title with the appropriate script code, using the `display_title` mechanism. ------------ -- Assumes that the scripts in "toBeTagged" will never occur in the Reconstruction namespace. -- (FIXME: Don't make assumptions like this, and if you need to do so, throw an error if the assumption is violated.) -- Avoid tagging ASCII as Hani even when it is tagged as Hani in the headword, as in [[check]]. The check for ASCII -- might need to be expanded to a check for any Latin characters and whitespace or punctuation. local display_title -- Where there are multiple headwords, use the script for the first. This assumes the first headword is similar to -- the pagename, and that headwords that are in different scripts from the pagename aren't first. This seems to be -- about the best we can do (alternatively we could potentially do script detection on the pagename). local dt_script = data.heads[1].sc local dt_script_code = dt_script:getCode() local page_non_ascii = namespace == "" and not page.pagename:find("^[%z\1-\127]+$") local unsupported_pagename, unsupported = page.full_raw_pagename:gsub("^Unsupported titles/", "") if unsupported == 1 and page.unsupported_titles[unsupported_pagename] then display_title = 'Unsupported titles/<span class="' .. dt_script_code .. '">' .. page.unsupported_titles[unsupported_pagename] .. '</span>' elseif page_non_ascii and m_headword_data.toBeTagged[dt_script_code] or (dt_script_code == "Jpan" and (text_in_script(page.pagename, "Hira") or text_in_script(page.pagename, "Kana"))) or (dt_script_code == "Kore" and text_in_script(page.pagename, "Hang")) then display_title = '<span class="' .. dt_script_code .. '">' .. page.full_raw_pagename .. '</span>' -- Keep Han entries region-neutral in the display title. elseif page_non_ascii and (dt_script_code == "Hant" or dt_script_code == "Hans") then display_title = '<span class="Hani">' .. page.full_raw_pagename .. '</span>' elseif namespace == "Reconstruction" then local matched display_title, matched = ugsub( page.full_raw_pagename, "^(Reconstruction:[^/]+/)(.+)$", function(before, term) return before .. tag_text(term, data.lang, dt_script) end ) if matched == 0 then display_title = nil end end -- FIXME: Generalize this. -- If the current language uses Aran (Nastaliq), e.g. Urdu, and there's more than one language on the page, don't -- set the display title because we don't want Nastaliq for terms that also exist in other languages that don't -- display in Nastaliq (e.g. Arabic or Persian). Because the word "Urdu" occurs near the end of the alphabet, Urdu -- fonts tend to override the fonts of other languages. FIXME: This is checking for more than one language on the -- page but instead needs to check if there are any languages using scripts other than Aran. if dt_script_code == "Aran" and page.L2_list.n > 1 then display_title = nil end if display_title then mw.getCurrentFrame():callParserFunction( "DISPLAYTITLE", display_title ) end ------------ 9. Insert additional categories. ------------ if data.force_cat_output then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/force cat output]] track("force cat output") end if has_redundant_head_param then if not data.no_redundant_head_cat then -- This is not the right way to go about this; too many exceptions and problems due to language-specific headword -- handling customization. If we want this, it should be opt-in by a given language passing in the default headword. -- insert(data.categories, full_langname .. " terms with redundant head parameter") end end if namespace == "Reconstruction" and not lang_reconstructed then insert(data.categories, full_langname .. " reconstructed terms") end -- If the first head is multiword (after removing links), maybe insert into "LANG multiword terms". if not data.nomultiwordcat and not is_varform_only and any_script_has_spaces and postype == "lemma" then local no_multiword_cat = m_headword_data.no_multiword_cat if not (no_multiword_cat[langcode] or no_multiword_cat[full_langcode]) then -- Check for spaces or hyphens, but exclude prefixes and suffixes. -- Use the pagename, not the head= value, because the latter may have extra -- junk in it, e.g. superscripted text that throws off the algorithm. local no_hyphen = m_headword_data.hyphen_not_multiword_sep -- Exclude hyphens if the data module states that they should for this language. local checkpattern = (no_hyphen[langcode] or no_hyphen[full_langcode]) and ".[%s፡]." or ".[%s%-፡]." local is_multiword = umatch(page.pagename, checkpattern) if is_multiword and not non_categorizable(page.full_raw_pagename) then insert(data.categories, full_langname .. " multiword terms") elseif not is_multiword then local long_word_threshold = m_headword_data.long_word_thresholds[langcode] or m_headword_data.long_word_thresholds[full_langcode] if long_word_threshold and ulen(page.pagename) >= long_word_threshold then insert(data.categories, "Long " .. full_langname .. " words") end end end end -- Determine whether to insert a category 'LANGNAME POS in SCRIPT'. If there are multiple heads, we may need to check -- each head, as the heads may (theoretically) have different scripts. local default_sccat = m_headword_data.default_sccat if data.sccat or not is_varform_only and (default_sccat[langcode] or langcode ~= full_langcode and default_sccat[full_langcode]) then local function needs_sccat(sccat_entry, sc) if sccat_entry == true or not sccat_entry then return sccat_entry end if type(sccat_entry) == "table" then local in_list = contains(sccat_entry, sc:getCode()) if sccat_entry[1] == "not" then in_list = not in_list end return in_list end return nil end for _, head in ipairs(data.heads) do -- First check the `sccat` specified at the {{head}} level. local this_needs_sccat = needs_sccat(data.sccat, head.sc) -- If that wasn't given, check the default sccat at the language level for the lang code. if this_needs_sccat == nil and not is_varform_only then this_needs_sccat = needs_sccat(default_sccat[langcode], head.sc) end -- If that wasn't found and the lang code is an etym code, check the default sccat at the parent language level. if this_needs_sccat == nil and not is_varform_only and langcode ~= full_langcode then this_needs_sccat = needs_sccat(default_sccat[full_langcode], head.sc) end if this_needs_sccat then insert(data.categories, full_langname .. " " .. data.pos_category .. " in " .. head.sc:getDisplayForm(data.lang)) end end end -- Reconstructed terms often use weird combinations of scripts and realistically aren't spelled so much as notated. if namespace ~= "Reconstruction" and not is_varform_only then -- Map from languages to a string containing the characters to ignore when considering whether a term has -- multiple written scripts in it. Typically these are Greek or Cyrillic letters used for their phonetic -- values. local characters_to_ignore = { ["aaq"] = "αάὰ", -- Penobscot (Algonquian) ["acy"] = "δθ", -- Cypriot Arabic ["aez"] = "β", -- Aeka (Trans-New Guinea) ["anc"] = "γ", -- Ngas (Chadic/Afroasiatic) ["aou"] = "χ", -- A'ou (Kra-Dai) ["art-blk"] = "ч", -- Bolak (conlang) ["awg"] = "β", -- Anguthimri (Pama-Nyungan) ["az"] = "ь", -- Azerbaijani (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ba"] = "ь", -- Bashkir (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["bhp"] = "β", -- Bima (Austronesian) ["bjz"] = "β", -- Baruga (Trans-New Guinea) ["byk"] = "θ", -- Biao (Kra-Dai) ["cdy"] = "θ", -- Chadong (Kra-Dai) ["chp"] = "θ", -- Chipewyan (Athabaskan) ["cjh"] = "χ", -- Upper Chehalis (Salishan) ["clm"] = "χ", -- Klallam (Salishan) ["col"] = "χ", -- Colombia-Wenatchi (Salishan) ["coo"] = "χθ", -- Comox (Salishan) ["crx"] = "θ", -- Carrier (Athabaskan) ["ets"] = "θ", -- Yekhee (Edoid/Niger-Congo) ["ett"] = "χ", -- Etruscan (isolate; in romanizations) ["fla"] = "χ", -- Montana Salish (Salishan) ["grt"] = "་", -- Garo (South Asian Sino-Tibetan) ["gmw-gts"] = "χ", -- Gottscheerish (Bavarian variant spoken in Slovenia) ["hur"] = "χθ", -- Halkomelem (Salishan) ["itc-psa"] = "f", -- Pre-Samnite (Italic; normally written in Greek) ["izh"] = "ь", -- Ingrian (Finnic) ["kic"] = "θ", -- Kickapoo (Algonquian) ["kk"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ky"] = "ь", -- Kyrgyz (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["lil"] = "χ", -- Lillooet (Salishan) ["lsi"] = "ꓹ", -- Lashi (Lolo-Burmese/Sino-Tibetan; represents a glottal stop) ["mhz"] = "β", -- Mor (Austronesian) ["mqn"] = "β", -- Moronene (Austronesian) ["neg"]= "ӡā", -- Negidal (Tungusic; normally in Cyrillic) ["oka"] = "χ", -- Okanagan (Salishan) ["ole"] = "θ", -- Olekha (Sino-Tibetan) ["oui"] = "γβ", -- Old Uyghur (Turkic; FIXME: others? E.g. Greek delta (δ)?) ["pox"] = "χ", -- Polabian (West Slavic) ["rif"] = "ε", -- Tarifit (Berber) ["rom"] = "Θθ", -- Romani (Indic: International Standard; two different thetas???) ["rpn"] = "β", -- Repanbitip (Austronesian) ["sah"] = "ь", -- Yakut (Turkic; 1929 - 1939 Latin spelling) ["sit-jap"] = "χ", -- Japhug (Sino-Tibetan) ["sjw"] = "θ", -- Shawnee (Algonquian) ["squ"] = "χ", -- Squamish (Salishan) ["str"] = "χθ", -- Saanich (Salishan) ["teh"] = "χ", -- Tehuelche (Chonan; spoken in Argentina) ["tep"] = "η", -- Tepecano (Uto-Aztecan) ["thp"] = "χ", -- Thompson (Salishan) ["tk"] = "ь", -- Turkmen (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["tt"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["twa"] = "χ", -- Twana (Salishan) ["wbl"] = "ы", -- Wakhi (Iranian) ["xbc"] = "ϸ", -- Bactrian (Iranian; represents š; normally written in Greek) ["yha"] = "θ", -- Baha (Kra-Dai) ["za"] = "зч", -- Zhuang (Tai/Kra-Dai); 1957-1982 alphabet used two Cyrillic letters (as well as some others like -- ƃ, ƅ, ƨ, ɯ and ɵ that look like Cyrillic or Greek but are actually Latin) ["zlw-slv"] = "χђћ", -- Slovincian (West Slavic; FIXME: χ is Greek, the other two are Cyrillic, but I'm not sure -- the currect characters are being chosen in the entry names) ["zng"] = "θ", -- Mang (Mon-Khmer) ["ztp"] = "θ", -- Loxicha Zapotec (Zapotecan) } -- Determine how many real scripts are found in the pagename, where we exclude symbols and such. We exclude -- scripts whose `character_category` is false as well as Zmth (mathematical notation symbols), which has a -- category of "Mathematical notation symbols". When counting scripts, we need to elide language-specific -- variants because e.g. Beng and as-Beng have slightly different characters but we don't want to consider them -- two different scripts (e.g. [[এৰ]] has two characters which are detected respectively as Beng and as-Beng). local seen_scripts = {} local num_seen_scripts = 0 local num_loops = 0 local canon_pagename = page.pagename local ch_to_ignore = characters_to_ignore[full_langcode] if ch_to_ignore then canon_pagename = ugsub(canon_pagename, "[" .. ch_to_ignore .. "]", "") end while true do if canon_pagename == "" or num_seen_scripts >= 2 or num_loops >= 10 then break end -- Make sure we don't get into a loop checking the same script over and over again; happens with e.g. [[ᠪᡳ]] num_loops = num_loops + 1 local pagename_script = find_best_script_without_lang(canon_pagename, "None only as last resort") local script_chars = pagename_script.characters if not script_chars then -- we are stuck; this happens with None break end local script_code = pagename_script:getCode() local replaced canon_pagename, replaced = ugsub(canon_pagename, "[" .. script_chars .. "]", "") if ( replaced and script_code ~= "Zmth" and (script_data or get_script_data())[script_code] and script_data[script_code].character_category ~= false ) then script_code = script_code:gsub("^.-%-", "") if not seen_scripts[script_code] then seen_scripts[script_code] = true num_seen_scripts = num_seen_scripts + 1 end end end if num_seen_scripts > 1 then insert(data.categories, full_langname .. " terms written in multiple scripts") end end -- Categorise for unusual characters. Takes into account combining characters, so that we can categorise for characters with diacritics that aren't encoded as atomic characters (e.g. U̠). These can be in two formats: single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + diacritic(s) + character). Each can have any number of diacritics. local standard = data.lang:getStandardCharacters() if not is_varform_only and standard and not non_categorizable(page.full_raw_pagename) then local function char_category(char) local specials = { ["#"] = "number sign", ["("] = "parentheses", [")"] = "parentheses", ["<"] = "angle brackets", [">"] = "angle brackets", ["["] = "square brackets", ["]"] = "square brackets", ["_"] = "underscore", ["{"] = "braces", ["|"] = "vertical line", ["}"] = "braces", ["ß"] = "ẞ", ["\205\133"] = "", -- this is UTF-8 for U+0345 ( ͅ) ["\239\191\189"] = "replacement character", } char = toNFD(char) :gsub(".[\128-\191]*", function(m) local new_m = specials[m] new_m = new_m or m:uupper() return new_m end) return toNFC(char) end if full_langcode ~= "hi" and full_langcode ~= "lo" then local standard_chars_scripts = {} for _, head in ipairs(data.heads) do standard_chars_scripts[head.sc:getCode()] = true end -- Iterate over the scripts, in case there is more than one (as they can have different sets of standard characters). for code in pairs(standard_chars_scripts) do local sc_standard = data.lang:getStandardCharacters(code) if sc_standard then if page.pagename_len > 1 then local explode_standard = {} local function explode(char) explode_standard[char] = true return "" end local sc_standard_exploded = ugsub(sc_standard, page.comb_chars.combined_double, explode) -- The following is correct; it relies on side-effecing the explode_standard[] table. ugsub(sc_standard_exploded, page.comb_chars.combined_single, explode):gsub(".[\128-\191]*", explode) local num_cat_inserted for char in pairs(page.explode_pagename) do if not explode_standard[char] then if char:find("[0-9]") then if not num_cat_inserted then insert(data.categories, full_langname .. " terms spelled with numbers") num_cat_inserted = true end elseif ufind(char, page.emoji_pattern) then insert(data.categories, full_langname .. " terms spelled with emoji") else local upper = char_category(char) if not explode_standard[upper] then char = upper end insert(data.categories, full_langname .. " terms spelled with " .. char) end end end end -- If a diacritic doesn't appear in any of the standard characters, also categorise for it generally. sc_standard = toNFD(sc_standard) for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_single) do if not umatch(sc_standard, diacritic) then insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic) end end for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_double) do if not umatch(sc_standard, diacritic) then insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic .. "◌") end end end end -- Ancient Greek, Hindi and Lao handled the old way for now, as their standard chars still need to be converted to the new format (because there are a lot of them). elseif ulen(page.pagename) ~= 1 then for character in ugmatch(page.pagename, "([^" .. standard .. "])") do local upper = char_category(character) if not umatch(upper, "[" .. standard .. "]") then character = upper end insert(data.categories, full_langname .. " terms spelled with " .. character) end end end if not is_varform_only and data.heads[1].sc:isSystem("alphabet") then local pagename, i = page.pagename:ulower(), 2 while umatch(pagename, "(%a)" .. ("%1"):rep(i)) do i = i + 1 insert(data.categories, full_langname .. " terms with " .. i .. " consecutive instances of the same letter") end end -- Categorise for palindromes if not is_varform_only and not data.nopalindromecat and namespace ~= "Reconstruction" and ulen(page.pagename) > 2 -- FIXME: Use of first script here seems hacky. What is the clean way of doing this in the presence of -- multiple scripts? and is_palindrome(page.pagename, data.lang, data.heads[1].sc) then insert(data.categories, full_langname .. " palindromes") end if namespace == "" and not lang_reconstructed then for _, head in ipairs(data.heads) do if page.full_raw_pagename ~= get_link_page(remove_links(head.term), data.lang, head.sc) then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch/LANGCODE]] track("pagename spelling mismatch", data.lang) break end end end -- Add red link category if called for and we're not a "large" page, where such checks are disabled. if data.checkredlinks and not m_headword_data.large_pages[m_headword_data.pagename] then local plposcat = type(data.checkredlinks) == "string" and data.checkredlinks or data.pos_category check_red_link_inflections_top_level(data, plposcat) end -- Add to various maintenance categories. export.maintenance_cats(page, data.lang, data.categories, data.whole_page_categories) ------------ 10. Format and return headwords, genders, inflections and categories. ------------ -- Format and return all the gathered information. This may add more categories (e.g. gender/number categories), -- so make sure we do it before evaluating `data.categories`. local text = '<span class="headword-line">' .. format_headword(data) .. format_headword_genders(data, is_varform_only) .. format_top_level_inflections(data) .. '</span>' -- Language-specific categories. local cats = format_categories( data.categories, data.lang, data.sort_key, page.encoded_pagename, data.force_cat_output or test_force_categories, data.heads[1].sc ) -- Language-agnostic categories. local whole_page_cats = format_categories( data.whole_page_categories, nil, "-" ) return text .. cats .. whole_page_cats end return export ntxp7rwc2bd097xfhto9jzd4sjnoa31 Module:palindromes 828 8020 54771 2026-09-25T22:19:26Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local data = mw.loadData("Module:palindromes/data") local function ignoreCharacters(term, lang, sc, langdata) term = mw.ustring.lower(term) term = mw.ustring.gsub(term, "[ ,%.%?!%%%-'\"]", "") -- Language-specific substitutions -- Ignore entire scripts (e.g. romaji in Japanese) if langdata.ignore then sc_name = sc and sc:getCode() or lang:findBestScript(term):getCode() for _, script in ipairs(langdata.ignore) do if script == sc_...' 54771 Scribunto text/plain local export = {} local data = mw.loadData("Module:palindromes/data") local function ignoreCharacters(term, lang, sc, langdata) term = mw.ustring.lower(term) term = mw.ustring.gsub(term, "[ ,%.%?!%%%-'\"]", "") -- Language-specific substitutions -- Ignore entire scripts (e.g. romaji in Japanese) if langdata.ignore then sc_name = sc and sc:getCode() or lang:findBestScript(term):getCode() for _, script in ipairs(langdata.ignore) do if script == sc_name then return "" end end end for i, from in ipairs(langdata.from or {}) do term = mw.ustring.gsub(term, from, langdata.to[i] or "") end return term end function export.is_palindrome(term, lang, sc) local langdata = data[lang:getCode()] or data[lang:getFullCode()] or {} -- Affixes aren't palindromes if mw.ustring.find(term, "^%-") or mw.ustring.find(term, "%-$") then return false end -- Remove punctuation and casing term = ignoreCharacters(term, lang, sc, langdata) local len = mw.ustring.len(term) if langdata.allow_repeated_char then -- Ignore single-character terms if len < 2 then return false end else -- Ignore terms that consist of just one character repeated -- This also excludes terms consisting of fewer than 3 characters if term == mw.ustring.rep(mw.ustring.sub(term, 1, 1), len) then return false end end local charlist = {} for c in mw.ustring.gmatch(term, ".") do table.insert(charlist, c) end for i = 1, math.floor(len / 2) do if charlist[i] ~= charlist[len - i + 1] then return false end end return true end return export ms2gvz5ktlkntyfgy8vn9mt955uxqdg Module:palindromes/data 828 8021 54772 2026-09-25T22:19:55Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local data = { ["ar"] = { allow_repeated_char = true, from = { "[أإآ]", "ؤ", "[ئى]", "ة", "ء", }, to = { "ا", "و", "ي", "ه", }, }, ["arc"] = { allow_repeated_char = true, from = { "ם", "ן", "ך", "ף", "ץ", "ﭏ", "װ", "ױ", "ײ", "[״׳־]", }, to = { "מ", "נ", "כ", "פ", "צ", "אל", "וו", "וי", "יי", } }, ["axm"] = { from =...' 54772 Scribunto text/plain local data = { ["ar"] = { allow_repeated_char = true, from = { "[أإآ]", "ؤ", "[ئى]", "ة", "ء", }, to = { "ا", "و", "ي", "ه", }, }, ["arc"] = { allow_repeated_char = true, from = { "ם", "ן", "ך", "ף", "ץ", "ﭏ", "װ", "ױ", "ײ", "[״׳־]", }, to = { "מ", "נ", "כ", "פ", "צ", "אל", "וו", "וי", "יי", } }, ["axm"] = { from = {"ու"}, to = {"ŭ"}, }, ["ca"] = { from = {"à", "[èé]", "[íï]", "[òó]", "[úü]", "ç", "l·l"}, to = {"a", "e", "i", "o", "u", "c", "ll"}, }, ["cmn"] = {ignore = {"Latn"}}, ["cs"] = { from = {"á", "é", "í", "ó", "[úů]", "ý", "ch"}, to = {"a", "e", "i", "o", "u", "y", "χ"}, }, ["de"] = { from = {"ä", "ö", "ü", "[ßẞ]"}, to = {"a", "o", "u", "ss"}, }, ["el"] = { from = { "[ᾳάᾴὰᾲᾶᾷἀᾀἄᾄἂᾂἆᾆἁᾁἅᾅἃᾃἇᾇᾱᾰἈᾈἌᾌἊᾊἎᾎἉᾉἍᾍἋᾋἏᾏᾹᾸ]", --uppercase characters are included due to this bug: https://bugs.php.net/bug.php?id=69267 "[έὲἐἔἒἑἕἓἘἜἚἙἝἛ]", "[ῃήῄὴῂῆῇἠᾐἤᾔἢᾒἦᾖἡᾑἥᾕἣᾓἧᾗἨᾘἬᾜἪᾚἮᾞἩᾙἭᾝἫᾛἯᾟ]", "[ίὶῖἰἴἲἶἱἵἳἷϊΐῒῗῑῐἸἼἺἾἹἽἻἿῙῘ]", "[όὸὀὄὂὁὅὃὈὌὊὉὍὋ]", "[ύὺῦὐὔὒὖὑὕὓὗϋΰῢῧῡῠὙὝὛὟῩῨ]", "[ῳώῴὼῲῶῷὠᾠὤᾤὢᾢὦᾦὡᾡὥᾥὣᾣὧᾧὨᾨὬᾬὪᾪὮᾮὩᾩὭᾭὫᾫὯᾯ]", "[ῥῤῬ]", "[ς]", "[́͂]" }, to = { "α", "ε", "η", "ι", "ο", "υ", "ω", "ρ", "σ" }, }, ["en"] = { from = {"[äàáâåā]", "[ëèéêē]", "[ïìíîī]", "[öòóôō]", "[üùúûū]", "æ" , "œ" , "[çč]", "ñ", "'"}, to = {"a", "e", "i", "o", "u", "ae", "oe", "c", "n"}, }, ["fr"] = { from = {"[áàâä]", "[éèêë]", "[íìîï]", "[óòôö]", "[úùûü]", "[ýỳŷÿ]", "ç", "æ", "œ", "'"}, to = {"a", "e", "i", "o", "u", "y", "c", "ae", "oe"}, }, ["fy"] = { from = {"[áàâä]", "[éèêë]", "[íìîï]", "[óòôö]", "[úùûü]", "[ýỳŷÿ]", "æ", "'"}, to = {"a", "e", "i", "o", "u", "y", "ae"}, }, ["grc"] = { from = { "[ᾳάᾴὰᾲᾶᾷἀᾀἄᾄἂᾂἆᾆἁᾁἅᾅἃᾃἇᾇᾱᾰἈᾈἌᾌἊᾊἎᾎἉᾉἍᾍἋᾋἏᾏᾹᾸ]", --uppercase characters are included due to this bug: https://bugs.php.net/bug.php?id=69267 "[έὲἐἔἒἑἕἓἘἜἚἙἝἛ]", "[ῃήῄὴῂῆῇἠᾐἤᾔἢᾒἦᾖἡᾑἥᾕἣᾓἧᾗἨᾘἬᾜἪᾚἮᾞἩᾙἭᾝἫᾛἯᾟ]", "[ίὶῖἰἴἲἶἱἵἳἷϊΐῒῗῑῐἸἼἺἾἹἽἻἿῙῘ]", "[όὸὀὄὂὁὅὃὈὌὊὉὍὋ]", "[ύὺῦὐὔὒὖὑὕὓὗϋΰῢῧῡῠὙὝὛὟῩῨ]", "[ῳώῴὼῲῶῷὠᾠὤᾤὢᾢὦᾦὡᾡὥᾥὣᾣὧᾧὨᾨὬᾬὪᾪὮᾮὩᾩὭᾭὫᾫὯᾯ]", "[ῥῤῬ]", "[ς]", "[́͂]" }, to = { "α", "ε", "η", "ι", "ο", "υ", "ω", "ρ", "σ" } }, ["he"] = { allow_repeated_char = true, from = { "ם", "ן", "ך", "ף", "ץ", "ﭏ", "װ", "ױ", "ײ", "[״׳־]", }, to = { "מ", "נ", "כ", "פ", "צ", "אל", "וו", "וי", "יי", } }, ["hu"] = { from = {"í", "ó", "ú", "ő", "ű", "ccs", "cs", "ggy", "gy", "lly", "ly", "nny", "ny", "ssz", "sz", "tty", "ty", "zzs", "zs", "ddzs", "dzs"}, to = {"i", "o", "u", "ö", "ü", "čč", "č", "ǰǰ", "ǰ", "ľľ", "ľ", "ňň", "ň", "šš", "š", "ťť", "ť", "žž", "ž", "ddž", "dž"}, }, ["hy"] = { from = {"ու", "եւ"}, to = {"ŭ", "և"}, }, ["ja"] = { allow_repeated_char = true, from = {'が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ', 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ', 'ゔ'}, to = {'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'う'}, ignore = {"Latn"}, }, ["la"] = { from = {"v", "j"}, to = {"u", "i"} }, ["nl"] = { from = {"[áàä]", "[éèë]", "[íìï]", "[óòö]", "[úùü]"}, to = {"a", "e", "i", "o", "u"}, }, ["pl"] = { from = {"ć", "ę", "ł", "ń", "ó", "ś", "[źż]"}, to = {"c", "e", "l", "n", "o", "s", "z"}, }, ["ru"] = { from = {"ё"}, to = {"е"}, }, ["xcl"] = { from = {"ու"}, to = {"ŭ"}, }, ["yi"] = { allow_repeated_char = true, from = { "ם", "ן", "ך", "ף", "ץ", "ﭏ", "װ", "ױ", "ײ", "[״׳־]", "[ִַָּֿׁׂ]", }, to = { "מ", "נ", "כ", "פ", "צ", "אל", "וו", "וי", "יי", } }, ["zh"] = { ignore = {"Latn"}, }, } return data lnfzz4yt7hlswzgb8pzp3idlrgkcnyk Module:table/extend 828 8022 54773 2026-09-25T22:20:47Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local insert = table.insert local select = select --[==[ Extend an existing list by a new list, modifying the existing list in-place. Compare the Python expression {list.extend(new_items)}.]==] return function(t, ...) local i = 0 if select("#", ...) < 2 then local list = ... while true do i = i + 1 local v = list[i] if v == nil then return t end insert(t, v) end else local pos, list = ... while true do i = i + 1 local v = li...' 54773 Scribunto text/plain local insert = table.insert local select = select --[==[ Extend an existing list by a new list, modifying the existing list in-place. Compare the Python expression {list.extend(new_items)}.]==] return function(t, ...) local i = 0 if select("#", ...) < 2 then local list = ... while true do i = i + 1 local v = list[i] if v == nil then return t end insert(t, v) end else local pos, list = ... while true do i = i + 1 local v = list[i] if v == nil then return t end insert(t, pos, v) pos = pos + 1 end end end adajacgjldccb0g5nl3tceeu9y5qxf5 Module:gender and number/data 828 8023 54774 2026-09-25T22:21:15Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip....' 54774 Scribunto text/plain local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip. If not, it will automatically be wrapped in this -- fashion. If `req` is true, a category "Requests for TYPE in LANG entries" will be generated, except for the code "?", -- which is special-cased; TYPE is "gender" unless the POS is "verb", in which case it is "aspect". data.codes = { ["?"] = {type = "other", req = true, display = '<abbr title="gender incomplete">?</abbr>'}, -- FIXME: The following should be either eliminated in favor of g! or converted to a general "gender/number unattested". ["?!"] = {type = "other", display = "gender unattested"}, -- Genders ["m"] = {type = "gender", cat = "masculine POS", display = '<abbr title="masculine gender">m</abbr>'}, ["f"] = {type = "gender", cat = "feminine POS", display = '<abbr title="feminine gender">f</abbr>'}, ["n"] = {type = "gender", cat = "neuter POS", display = '<abbr title="neuter gender">n</abbr>'}, ["c"] = {type = "gender", cat = "common-gender POS", display = '<abbr title="common gender">c</abbr>'}, ["gneut"] = {type = "gender", cat = "gender-neutral POS", display = "gender-neutral"}, ["g!"] = {type = "gender", display = "gender unattested"}, ["g?"] = {type = "gender", req = true, display = "gender unspecified"}, -- Animacy -- Animate = either animal or personal (for Russian, etc.) ["an"] = {type = "animacy", cat = "animate POS", display = '<abbr title="animate">anim</abbr>'}, ["in"] = {type = "animacy", cat = "inanimate POS", display = '<abbr title="inanimate">inan</abbr>'}, -- Animal (for Ukrainian, Belarusian, Polish, etc.) ["anml"] = {type = "animacy", cat = "animal POS", display = "animal"}, -- Personal (for Ukrainian, Belarusian, Polish, etc.) ["pr"] = {type = "animacy", cat = "personal POS", display = '<abbr title="personal">pers</abbr>'}, ["np"] = {type = "animacy", cat = "nonpersonal POS", display = '<abbr title="nonpersonal">npers</abbr>'}, ["an!"] = {type = "animacy", display = "animacy unattested"}, ["an?"] = {type = "animacy", req = true, display = "animacy unspecified"}, -- Definiteness ["def"] = {type = "definiteness", cat = "definite POS", display = '<abbr title="definite">def</abbr>'}, ["indef"] = {type = "definiteness", cat = "indefinite POS", display = '<abbr title="indefinite">indef</abbr>'}, -- Virility (for Polish) ["vr"] = {type = "virility", cat = "virile POS", display = '<abbr title="virile (= masculine personal)">vir</abbr>'}, ["nv"] = {type = "virility", cat = "nonvirile POS", display = '<abbr title="nonvirile (= other than masculine personal)">nvir</abbr>'}, -- Numbers ["s"] = {type = "number", display = '<abbr title="singular number">sg</abbr>'}, ["d"] = {type = "number", cat = "dualia tantum", display = '<abbr title="dual number">du</abbr>'}, ["p"] = {type = "number", cat = "pluralia tantum", display = '<abbr title="plural number">pl</abbr>'}, ["num!"] = {type = "number", display = "number unattested"}, ["num?"] = {type = "number", req = true, display = "number unspecified"}, -- Verb qualifiers ["impf"] = {type = "aspect", cat = "imperfective POS", display = '<abbr title="imperfective aspect">impf</abbr>'}, ["pf"] = {type = "aspect", cat = "perfective POS", display = '<abbr title="perfective aspect">pf</abbr>'}, ["asp!"] = {type = "aspect", display = "aspect unattested"}, ["asp?"] = {type = "aspect", req = true, display = "aspect unspecified"}, } -- Combined codes that are equivalent to giving multiple specs. `mf` is the same as specifying two separate specs, -- one with `m` in it and the other with `f`. `mfbysense` is similar but is used for nouns that can be either masculine -- or feminine according as to whether they refer to masculine or feminine beings. local combinations = { ["biasp"] = {codes = {"impf", "pf"}}, ["anin"] = {codes = {"an", "in"}}, -- "bianimate" doesn't exist as a linguistic term } for _, comb in ipairs{"mf", "mn", "fm", "fn", "cn", "nm", "nf", "nc", "mfn", "mnf", "fmn", "fnm", "nmf", "nfm"} do local codes = {} for ch in comb:gmatch(".") do insert(codes, ch) end combinations[comb] = {codes = codes} combinations[comb .. "equiv"] = {codes = codes, display = '<abbr title="different genders do not affect the meaning">same meaning</abbr>'} if comb == "mf" or comb == "fm" then combinations[comb .. "bysense"] = {codes = codes, cat = "masculine and feminine POS by sense", display = '<abbr title="according to the gender of the referent">by sense</abbr>'} end end data.combinations = combinations -- Categories when multiple gender/number codes of a given type occur in different specs (two or more of the same type -- cannot occur in a single spec). data.multicode_cats = { ["gender"] = "POS with multiple genders", ["animacy"] = "POS with multiple animacies", ["aspect"] = "biaspectual POS", } return data ez8li72lhxsz7uadcg8lztfyl3hm08a 54775 54774 2026-09-25T22:31:55Z Deadend0914 7211 54775 Scribunto text/plain local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip. If not, it will automatically be wrapped in this -- fashion. If `req` is true, a category "Requests for TYPE in LANG entries" will be generated, except for the code "?", -- which is special-cased; TYPE is "gender" unless the POS is "verb", in which case it is "aspect". data.codes = { ["?"] = {type = "other", req = true, display = '<abbr title="gender incomplete">?</abbr>'}, -- FIXME: The following should be either eliminated in favor of g! or converted to a general "gender/number unattested". ["?!"] = {type = "other", display = "gender unattested"}, -- Genders ["wer"] = {type = "gender", cat = "masculine POS", display = '<abbr title="masculine gender">m</abbr>'}, ["wif"] = {type = "gender", cat = "feminine POS", display = '<abbr title="feminine gender">f</abbr>'}, ["n"] = {type = "gender", cat = "neuter POS", display = '<abbr title="neuter gender">n</abbr>'}, ["c"] = {type = "gender", cat = "common-gender POS", display = '<abbr title="common gender">c</abbr>'}, ["gneut"] = {type = "gender", cat = "gender-neutral POS", display = "gender-neutral"}, ["g!"] = {type = "gender", display = "gender unattested"}, ["g?"] = {type = "gender", req = true, display = "gender unspecified"}, -- Animacy -- Animate = either animal or personal (for Russian, etc.) ["an"] = {type = "animacy", cat = "animate POS", display = '<abbr title="animate">anim</abbr>'}, ["in"] = {type = "animacy", cat = "inanimate POS", display = '<abbr title="inanimate">inan</abbr>'}, -- Animal (for Ukrainian, Belarusian, Polish, etc.) ["anml"] = {type = "animacy", cat = "animal POS", display = "animal"}, -- Personal (for Ukrainian, Belarusian, Polish, etc.) ["pr"] = {type = "animacy", cat = "personal POS", display = '<abbr title="personal">pers</abbr>'}, ["np"] = {type = "animacy", cat = "nonpersonal POS", display = '<abbr title="nonpersonal">npers</abbr>'}, ["an!"] = {type = "animacy", display = "animacy unattested"}, ["an?"] = {type = "animacy", req = true, display = "animacy unspecified"}, -- Definiteness ["def"] = {type = "definiteness", cat = "definite POS", display = '<abbr title="definite">def</abbr>'}, ["indef"] = {type = "definiteness", cat = "indefinite POS", display = '<abbr title="indefinite">indef</abbr>'}, -- Virility (for Polish) ["vr"] = {type = "virility", cat = "virile POS", display = '<abbr title="virile (= masculine personal)">vir</abbr>'}, ["nv"] = {type = "virility", cat = "nonvirile POS", display = '<abbr title="nonvirile (= other than masculine personal)">nvir</abbr>'}, -- Numbers ["s"] = {type = "number", display = '<abbr title="ānfeald rime">sg</abbr>'}, ["d"] = {type = "number", cat = "dualia tantum", display = '<abbr title="twifeald rime">du</abbr>'}, ["p"] = {type = "number", cat = "pluralia tantum", display = '<abbr title="manigfeald rime">pl</abbr>'}, ["num!"] = {type = "number", display = "number unattested"}, ["num?"] = {type = "number", req = true, display = "number unspecified"}, -- Verb qualifiers ["impf"] = {type = "aspect", cat = "imperfective POS", display = '<abbr title="imperfective aspect">impf</abbr>'}, ["pf"] = {type = "aspect", cat = "perfective POS", display = '<abbr title="perfective aspect">pf</abbr>'}, ["asp!"] = {type = "aspect", display = "aspect unattested"}, ["asp?"] = {type = "aspect", req = true, display = "aspect unspecified"}, } -- Combined codes that are equivalent to giving multiple specs. `mf` is the same as specifying two separate specs, -- one with `m` in it and the other with `f`. `mfbysense` is similar but is used for nouns that can be either masculine -- or feminine according as to whether they refer to masculine or feminine beings. local combinations = { ["biasp"] = {codes = {"impf", "pf"}}, ["anin"] = {codes = {"an", "in"}}, -- "bianimate" doesn't exist as a linguistic term } for _, comb in ipairs{"mf", "mn", "fm", "fn", "cn", "nm", "nf", "nc", "mfn", "mnf", "fmn", "fnm", "nmf", "nfm"} do local codes = {} for ch in comb:gmatch(".") do insert(codes, ch) end combinations[comb] = {codes = codes} combinations[comb .. "equiv"] = {codes = codes, display = '<abbr title="different genders do not affect the meaning">same meaning</abbr>'} if comb == "mf" or comb == "fm" then combinations[comb .. "bysense"] = {codes = codes, cat = "masculine and feminine POS by sense", display = '<abbr title="according to the gender of the referent">by sense</abbr>'} end end data.combinations = combinations -- Categories when multiple gender/number codes of a given type occur in different specs (two or more of the same type -- cannot occur in a single spec). data.multicode_cats = { ["gender"] = "POS with multiple genders", ["animacy"] = "POS with multiple animacies", ["aspect"] = "biaspectual POS", } return data reqqocrk3u1qcf6gviljuo2jd91cf5c 54776 54775 2026-09-25T22:35:44Z Deadend0914 7211 54776 Scribunto text/plain local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip. If not, it will automatically be wrapped in this -- fashion. If `req` is true, a category "Requests for TYPE in LANG entries" will be generated, except for the code "?", -- which is special-cased; TYPE is "gender" unless the POS is "verb", in which case it is "aspect". data.codes = { ["?"] = {type = "other", req = true, display = '<abbr title="gender incomplete">?</abbr>'}, -- FIXME: The following should be either eliminated in favor of g! or converted to a general "gender/number unattested". ["?!"] = {type = "other", display = "gender unattested"}, -- Genders ["m"] = {type = "gender", cat = "masculine POS", display = '<abbr title="werlic cynn">m</abbr>'}, ["f"] = {type = "gender", cat = "feminine POS", display = '<abbr title="wiflic cynn">f</abbr>'}, ["n"] = {type = "gender", cat = "neuter POS", display = '<abbr title="nāhwæðer cynn">n</abbr>'}, ["c"] = {type = "gender", cat = "common-gender POS", display = '<abbr title="common gender">c</abbr>'}, ["gneut"] = {type = "gender", cat = "gender-neutral POS", display = "gender-neutral"}, ["g!"] = {type = "gender", display = "gender unattested"}, ["g?"] = {type = "gender", req = true, display = "gender unspecified"}, -- Animacy -- Animate = either animal or personal (for Russian, etc.) ["an"] = {type = "animacy", cat = "animate POS", display = '<abbr title="animate">anim</abbr>'}, ["in"] = {type = "animacy", cat = "inanimate POS", display = '<abbr title="inanimate">inan</abbr>'}, -- Animal (for Ukrainian, Belarusian, Polish, etc.) ["anml"] = {type = "animacy", cat = "animal POS", display = "animal"}, -- Personal (for Ukrainian, Belarusian, Polish, etc.) ["pr"] = {type = "animacy", cat = "personal POS", display = '<abbr title="personal">pers</abbr>'}, ["np"] = {type = "animacy", cat = "nonpersonal POS", display = '<abbr title="nonpersonal">npers</abbr>'}, ["an!"] = {type = "animacy", display = "animacy unattested"}, ["an?"] = {type = "animacy", req = true, display = "animacy unspecified"}, -- Definiteness ["def"] = {type = "definiteness", cat = "definite POS", display = '<abbr title="definite">def</abbr>'}, ["indef"] = {type = "definiteness", cat = "indefinite POS", display = '<abbr title="indefinite">indef</abbr>'}, -- Virility (for Polish) ["vr"] = {type = "virility", cat = "virile POS", display = '<abbr title="virile (= masculine personal)">vir</abbr>'}, ["nv"] = {type = "virility", cat = "nonvirile POS", display = '<abbr title="nonvirile (= other than masculine personal)">nvir</abbr>'}, -- Numbers ["s"] = {type = "number", display = '<abbr title="ānfeald rime">sg</abbr>'}, ["d"] = {type = "number", cat = "dualia tantum", display = '<abbr title="twifeald rime">du</abbr>'}, ["p"] = {type = "number", cat = "pluralia tantum", display = '<abbr title="manigfeald rime">pl</abbr>'}, ["num!"] = {type = "number", display = "number unattested"}, ["num?"] = {type = "number", req = true, display = "number unspecified"}, -- Verb qualifiers ["impf"] = {type = "aspect", cat = "imperfective POS", display = '<abbr title="imperfective aspect">impf</abbr>'}, ["pf"] = {type = "aspect", cat = "perfective POS", display = '<abbr title="perfective aspect">pf</abbr>'}, ["asp!"] = {type = "aspect", display = "aspect unattested"}, ["asp?"] = {type = "aspect", req = true, display = "aspect unspecified"}, } -- Combined codes that are equivalent to giving multiple specs. `mf` is the same as specifying two separate specs, -- one with `m` in it and the other with `f`. `mfbysense` is similar but is used for nouns that can be either masculine -- or feminine according as to whether they refer to masculine or feminine beings. local combinations = { ["biasp"] = {codes = {"impf", "pf"}}, ["anin"] = {codes = {"an", "in"}}, -- "bianimate" doesn't exist as a linguistic term } for _, comb in ipairs{"mf", "mn", "fm", "fn", "cn", "nm", "nf", "nc", "mfn", "mnf", "fmn", "fnm", "nmf", "nfm"} do local codes = {} for ch in comb:gmatch(".") do insert(codes, ch) end combinations[comb] = {codes = codes} combinations[comb .. "equiv"] = {codes = codes, display = '<abbr title="different genders do not affect the meaning">same meaning</abbr>'} if comb == "mf" or comb == "fm" then combinations[comb .. "bysense"] = {codes = codes, cat = "masculine and feminine POS by sense", display = '<abbr title="according to the gender of the referent">by sense</abbr>'} end end data.combinations = combinations -- Categories when multiple gender/number codes of a given type occur in different specs (two or more of the same type -- cannot occur in a single spec). data.multicode_cats = { ["gender"] = "POS with multiple genders", ["animacy"] = "POS with multiple animacies", ["aspect"] = "biaspectual POS", } return data jzk6p1ninsf0bivga7e6fd78zzq6lko 54777 54776 2026-09-25T22:36:20Z Deadend0914 7211 54777 Scribunto text/plain local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip. If not, it will automatically be wrapped in this -- fashion. If `req` is true, a category "Requests for TYPE in LANG entries" will be generated, except for the code "?", -- which is special-cased; TYPE is "gender" unless the POS is "verb", in which case it is "aspect". data.codes = { ["?"] = {type = "other", req = true, display = '<abbr title="gender incomplete">?</abbr>'}, -- FIXME: The following should be either eliminated in favor of g! or converted to a general "gender/number unattested". ["?!"] = {type = "other", display = "gender unattested"}, -- Genders ["wer"] = {type = "gender", cat = "masculine POS", display = '<abbr title="werlic cynn">m</abbr>'}, ["wif"] = {type = "gender", cat = "feminine POS", display = '<abbr title="wiflic cynn">f</abbr>'}, ["n"] = {type = "gender", cat = "neuter POS", display = '<abbr title="nāhwæðer cynn">n</abbr>'}, ["c"] = {type = "gender", cat = "common-gender POS", display = '<abbr title="common gender">c</abbr>'}, ["gneut"] = {type = "gender", cat = "gender-neutral POS", display = "gender-neutral"}, ["g!"] = {type = "gender", display = "gender unattested"}, ["g?"] = {type = "gender", req = true, display = "gender unspecified"}, -- Animacy -- Animate = either animal or personal (for Russian, etc.) ["an"] = {type = "animacy", cat = "animate POS", display = '<abbr title="animate">anim</abbr>'}, ["in"] = {type = "animacy", cat = "inanimate POS", display = '<abbr title="inanimate">inan</abbr>'}, -- Animal (for Ukrainian, Belarusian, Polish, etc.) ["anml"] = {type = "animacy", cat = "animal POS", display = "animal"}, -- Personal (for Ukrainian, Belarusian, Polish, etc.) ["pr"] = {type = "animacy", cat = "personal POS", display = '<abbr title="personal">pers</abbr>'}, ["np"] = {type = "animacy", cat = "nonpersonal POS", display = '<abbr title="nonpersonal">npers</abbr>'}, ["an!"] = {type = "animacy", display = "animacy unattested"}, ["an?"] = {type = "animacy", req = true, display = "animacy unspecified"}, -- Definiteness ["def"] = {type = "definiteness", cat = "definite POS", display = '<abbr title="definite">def</abbr>'}, ["indef"] = {type = "definiteness", cat = "indefinite POS", display = '<abbr title="indefinite">indef</abbr>'}, -- Virility (for Polish) ["vr"] = {type = "virility", cat = "virile POS", display = '<abbr title="virile (= masculine personal)">vir</abbr>'}, ["nv"] = {type = "virility", cat = "nonvirile POS", display = '<abbr title="nonvirile (= other than masculine personal)">nvir</abbr>'}, -- Numbers ["s"] = {type = "number", display = '<abbr title="ānfeald rime">sg</abbr>'}, ["d"] = {type = "number", cat = "dualia tantum", display = '<abbr title="twifeald rime">du</abbr>'}, ["p"] = {type = "number", cat = "pluralia tantum", display = '<abbr title="manigfeald rime">pl</abbr>'}, ["num!"] = {type = "number", display = "number unattested"}, ["num?"] = {type = "number", req = true, display = "number unspecified"}, -- Verb qualifiers ["impf"] = {type = "aspect", cat = "imperfective POS", display = '<abbr title="imperfective aspect">impf</abbr>'}, ["pf"] = {type = "aspect", cat = "perfective POS", display = '<abbr title="perfective aspect">pf</abbr>'}, ["asp!"] = {type = "aspect", display = "aspect unattested"}, ["asp?"] = {type = "aspect", req = true, display = "aspect unspecified"}, } -- Combined codes that are equivalent to giving multiple specs. `mf` is the same as specifying two separate specs, -- one with `m` in it and the other with `f`. `mfbysense` is similar but is used for nouns that can be either masculine -- or feminine according as to whether they refer to masculine or feminine beings. local combinations = { ["biasp"] = {codes = {"impf", "pf"}}, ["anin"] = {codes = {"an", "in"}}, -- "bianimate" doesn't exist as a linguistic term } for _, comb in ipairs{"mf", "mn", "fm", "fn", "cn", "nm", "nf", "nc", "mfn", "mnf", "fmn", "fnm", "nmf", "nfm"} do local codes = {} for ch in comb:gmatch(".") do insert(codes, ch) end combinations[comb] = {codes = codes} combinations[comb .. "equiv"] = {codes = codes, display = '<abbr title="different genders do not affect the meaning">same meaning</abbr>'} if comb == "mf" or comb == "fm" then combinations[comb .. "bysense"] = {codes = codes, cat = "masculine and feminine POS by sense", display = '<abbr title="according to the gender of the referent">by sense</abbr>'} end end data.combinations = combinations -- Categories when multiple gender/number codes of a given type occur in different specs (two or more of the same type -- cannot occur in a single spec). data.multicode_cats = { ["gender"] = "POS with multiple genders", ["animacy"] = "POS with multiple animacies", ["aspect"] = "biaspectual POS", } return data 433lk33jh0tbudlwd2nq6dnlnqdxe55 54779 54777 2026-09-25T22:38:27Z Deadend0914 7211 54779 Scribunto text/plain local data = {} local insert = table.insert -- A list of all possible "parts" that a specification can be made out of. For each part, we list the class it's in -- (gender, animacy, etc.), the associated category (if any) and the display form. In a given gender/number spec, only -- one part of each class is allowed. `display` is how the code is diplayed to the user and should normally be wrapped -- in <abbr title="tooltip">...</abbr> with an explanatory tooltip. If not, it will automatically be wrapped in this -- fashion. If `req` is true, a category "Requests for TYPE in LANG entries" will be generated, except for the code "?", -- which is special-cased; TYPE is "gender" unless the POS is "verb", in which case it is "aspect". data.codes = { ["?"] = {type = "other", req = true, display = '<abbr title="gender incomplete">?</abbr>'}, -- FIXME: The following should be either eliminated in favor of g! or converted to a general "gender/number unattested". ["?!"] = {type = "other", display = "gender unattested"}, -- Genders ["wer"] = {type = "gender", cat = "masculine POS", display = '<abbr title="werlic cynn">wer</abbr>'}, ["wif"] = {type = "gender", cat = "feminine POS", display = '<abbr title="wiflic cynn">wif</abbr>'}, ["n"] = {type = "gender", cat = "neuter POS", display = '<abbr title="nāhwæðer cynn">n</abbr>'}, ["c"] = {type = "gender", cat = "common-gender POS", display = '<abbr title="common gender">c</abbr>'}, ["gneut"] = {type = "gender", cat = "gender-neutral POS", display = "gender-neutral"}, ["g!"] = {type = "gender", display = "gender unattested"}, ["g?"] = {type = "gender", req = true, display = "gender unspecified"}, -- Animacy -- Animate = either animal or personal (for Russian, etc.) ["an"] = {type = "animacy", cat = "animate POS", display = '<abbr title="animate">anim</abbr>'}, ["in"] = {type = "animacy", cat = "inanimate POS", display = '<abbr title="inanimate">inan</abbr>'}, -- Animal (for Ukrainian, Belarusian, Polish, etc.) ["anml"] = {type = "animacy", cat = "animal POS", display = "animal"}, -- Personal (for Ukrainian, Belarusian, Polish, etc.) ["pr"] = {type = "animacy", cat = "personal POS", display = '<abbr title="personal">pers</abbr>'}, ["np"] = {type = "animacy", cat = "nonpersonal POS", display = '<abbr title="nonpersonal">npers</abbr>'}, ["an!"] = {type = "animacy", display = "animacy unattested"}, ["an?"] = {type = "animacy", req = true, display = "animacy unspecified"}, -- Definiteness ["def"] = {type = "definiteness", cat = "definite POS", display = '<abbr title="definite">def</abbr>'}, ["indef"] = {type = "definiteness", cat = "indefinite POS", display = '<abbr title="indefinite">indef</abbr>'}, -- Virility (for Polish) ["vr"] = {type = "virility", cat = "virile POS", display = '<abbr title="virile (= masculine personal)">vir</abbr>'}, ["nv"] = {type = "virility", cat = "nonvirile POS", display = '<abbr title="nonvirile (= other than masculine personal)">nvir</abbr>'}, -- Numbers ["s"] = {type = "number", display = '<abbr title="ānfeald rime">sg</abbr>'}, ["d"] = {type = "number", cat = "dualia tantum", display = '<abbr title="twifeald rime">du</abbr>'}, ["p"] = {type = "number", cat = "pluralia tantum", display = '<abbr title="manigfeald rime">pl</abbr>'}, ["num!"] = {type = "number", display = "number unattested"}, ["num?"] = {type = "number", req = true, display = "number unspecified"}, -- Verb qualifiers ["impf"] = {type = "aspect", cat = "imperfective POS", display = '<abbr title="imperfective aspect">impf</abbr>'}, ["pf"] = {type = "aspect", cat = "perfective POS", display = '<abbr title="perfective aspect">pf</abbr>'}, ["asp!"] = {type = "aspect", display = "aspect unattested"}, ["asp?"] = {type = "aspect", req = true, display = "aspect unspecified"}, } -- Combined codes that are equivalent to giving multiple specs. `mf` is the same as specifying two separate specs, -- one with `m` in it and the other with `f`. `mfbysense` is similar but is used for nouns that can be either masculine -- or feminine according as to whether they refer to masculine or feminine beings. local combinations = { ["biasp"] = {codes = {"impf", "pf"}}, ["anin"] = {codes = {"an", "in"}}, -- "bianimate" doesn't exist as a linguistic term } for _, comb in ipairs{"mf", "mn", "fm", "fn", "cn", "nm", "nf", "nc", "mfn", "mnf", "fmn", "fnm", "nmf", "nfm"} do local codes = {} for ch in comb:gmatch(".") do insert(codes, ch) end combinations[comb] = {codes = codes} combinations[comb .. "equiv"] = {codes = codes, display = '<abbr title="different genders do not affect the meaning">same meaning</abbr>'} if comb == "mf" or comb == "fm" then combinations[comb .. "bysense"] = {codes = codes, cat = "masculine and feminine POS by sense", display = '<abbr title="according to the gender of the referent">by sense</abbr>'} end end data.combinations = combinations -- Categories when multiple gender/number codes of a given type occur in different specs (two or more of the same type -- cannot occur in a single spec). data.multicode_cats = { ["gender"] = "POS with multiple genders", ["animacy"] = "POS with multiple animacies", ["aspect"] = "biaspectual POS", } return data dxs6tyhs8zgb6w5k9gqkrujkx9bj3ax Bysen:ang-decl-noun-z-n 10 8024 54783 2026-09-25T22:45:38Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{ang-decl-noun<!-- -->|type=strong ''z''-stem<!-- -->|1={{{nomsg|{{{1}}}}}}<!-- -->|3={{{nomsg|{{{1}}}}}}<!-- -->|5={{{1}}}es<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}ru<!-- -->|4={{{1}}}ru<!-- -->|6={{{1}}}ra<!-- -->|8={{{1}}}rum<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|z-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude>' 54783 wikitext text/x-wiki {{ang-decl-noun<!-- -->|type=strong ''z''-stem<!-- -->|1={{{nomsg|{{{1}}}}}}<!-- -->|3={{{nomsg|{{{1}}}}}}<!-- -->|5={{{1}}}es<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}ru<!-- -->|4={{{1}}}ru<!-- -->|6={{{1}}}ra<!-- -->|8={{{1}}}rum<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|z-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> su1camu2jbf6seb2uawef21f71on7o5 nytt 0 8025 54786 2026-09-26T01:17:31Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|ang}}== ===Rihtstefn=== * IPA: /ˈxɑb.bɑn/ ===Wordstǣr 1=== Of Ealdoric Germanisce ''[[*nuti]]'' ===Tōgeīecendlic=== '''nytt''' # tō hæbbenne [[framigendlic]] [[notu]] ===Declīnung=== {{ang-adecl|nytt}} ===Wendunga=== * Nīwenglisc: [[useful]] ===Wordstǣr 2=== Of Ealdoric Germanisce ''[[*nutjō]]'' ===Wiflic Nama=== {{ang-noun|wif|head=nytt}} # [[notu]] ===Declīnung=== {{ang-decl-noun-o-f|nytt}} ===Wendunga=== # Nīwenglisc: [[use]]' 54786 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== * IPA: /ˈxɑb.bɑn/ ===Wordstǣr 1=== Of Ealdoric Germanisce ''[[*nuti]]'' ===Tōgeīecendlic=== '''nytt''' # tō hæbbenne [[framigendlic]] [[notu]] ===Declīnung=== {{ang-adecl|nytt}} ===Wendunga=== * Nīwenglisc: [[useful]] ===Wordstǣr 2=== Of Ealdoric Germanisce ''[[*nutjō]]'' ===Wiflic Nama=== {{ang-noun|wif|head=nytt}} # [[notu]] ===Declīnung=== {{ang-decl-noun-o-f|nytt}} ===Wendunga=== # Nīwenglisc: [[use]] p2bcikxqntf8fou5jzoua9zjzkfa338 54787 54786 2026-09-26T01:17:52Z Deadend0914 7211 /* Rihtstefn */ 54787 wikitext text/x-wiki =={{sprǣc|ang}}== ===Rihtstefn=== * IPA: /nytt/ ===Wordstǣr 1=== Of Ealdoric Germanisce ''[[*nuti]]'' ===Tōgeīecendlic=== '''nytt''' # tō hæbbenne [[framigendlic]] [[notu]] ===Declīnung=== {{ang-adecl|nytt}} ===Wendunga=== * Nīwenglisc: [[useful]] ===Wordstǣr 2=== Of Ealdoric Germanisce ''[[*nutjō]]'' ===Wiflic Nama=== {{ang-noun|wif|head=nytt}} # [[notu]] ===Declīnung=== {{ang-decl-noun-o-f|nytt}} ===Wendunga=== # Nīwenglisc: [[use]] 8h95oesnu7m8qjuhbt5nb79zumigtme ground 0 8026 54788 2026-09-26T01:42:24Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc:en}}== ===Rihtstefn=== * IPA: /ɡɹaʊnd/ ===Wordstǣr=== Of {{inh|en|enm|ground}} ===Nama=== {{en-noun|~}} # [[grund]] ===Wendunga=== * Englisc: [[grund]]' 54788 wikitext text/x-wiki =={{sprǣc:en}}== ===Rihtstefn=== * IPA: /ɡɹaʊnd/ ===Wordstǣr=== Of {{inh|en|enm|ground}} ===Nama=== {{en-noun|~}} # [[grund]] ===Wendunga=== * Englisc: [[grund]] o5nbrmkqzb800ydt3lpqcc9zzdn8yha 54790 54788 2026-09-26T01:44:30Z Deadend0914 7211 /* {{sprǣc:en}} */ 54790 wikitext text/x-wiki ==Nīwenglisc== ===Rihtstefn=== * IPA: /ɡɹaʊnd/ ===Wordstǣr=== Of {{inh|en|enm|ground}} ===Nama=== {{en-noun|~}} # [[grund]] ===Wendunga=== * Englisc: [[grund]] syfqxunsjsgzl43u5ir8y23tlpyp0wk tōgeīecendlic 0 8027 54792 2026-09-26T01:59:04Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|ang}}== ===Āwendednessa=== * tōġeīċendlīċ ===Rihtstefn=== * IPA: /toː.jeˈi͜yː.t͡ʃend.liːt͡ʃ/ ===Wordstǣr=== Of ''[[tō]]'' and ''[[geīeċendlīc]]'' ===Tōgeīecendlic=== # word hwelc cásus word on stæfcræft ===Declīnung=== {{ang-adecl|tōgeīecendlic}} ===Wendunga=== * Nīwenglisc: [[adjective]]' 54792 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * tōġeīċendlīċ ===Rihtstefn=== * IPA: /toː.jeˈi͜yː.t͡ʃend.liːt͡ʃ/ ===Wordstǣr=== Of ''[[tō]]'' and ''[[geīeċendlīc]]'' ===Tōgeīecendlic=== # word hwelc cásus word on stæfcræft ===Declīnung=== {{ang-adecl|tōgeīecendlic}} ===Wendunga=== * Nīwenglisc: [[adjective]] lur5yzi7ql4m6g7goekgnoqcirefd7t declension 0 8028 54793 2026-09-26T02:06:00Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * IPA: /dɪˈklɛn.ʃən/ ===Wordstǣr=== Of {{inh|en|enm|declinson}}, of {{der|en|frm|declinaison}} ===Nama=== {{en-noun|head=declension}} # [[declīnung]] ===Wendunga=== * Englisc: [[declīnung]]' 54793 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /dɪˈklɛn.ʃən/ ===Wordstǣr=== Of {{inh|en|enm|declinson}}, of {{der|en|frm|declinaison}} ===Nama=== {{en-noun|head=declension}} # [[declīnung]] ===Wendunga=== * Englisc: [[declīnung]] ag8hpx0beny3sk75g5j52l11yxo7wl9 Bysen:ang-tdecl 10 8029 54794 2026-09-26T02:07:15Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:ang-adjective|show}}<!--' 54794 wikitext text/x-wiki {{#invoke:ang-adjective|show}}<!-- bb7fgjovs58xiy7oadpfd39umhdrpmb 54796 54794 2026-09-26T02:22:20Z Deadend0914 7211 54796 wikitext text/x-wiki {{#invoke:ang-togeiecendlic|show}}<!-- ei8hc8h2rahzgr2oon901qd4ad4n9ii Module:ang-togeiecendlic 828 8030 54795 2026-09-26T02:14:37Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[=[ This module contains functions for creating inflection tables for Englisc tōgeīecendlic. It implements {{ang-tdecl}}. Author: Benwing2 External entry points: show(): For {{ang-tdecl}} make_table(): For {{ang-decl-tog-tabule}} (no longer in existence) ]=]-- local m_links = require("Module:tígunga") local strutils = require("Module:string utilities") local m_table = require("Module:tabule") local lang = require("Module:sprǣca").getByCode("ang")...' 54795 Scribunto text/plain --[=[ This module contains functions for creating inflection tables for Englisc tōgeīecendlic. It implements {{ang-tdecl}}. Author: Benwing2 External entry points: show(): For {{ang-tdecl}} make_table(): For {{ang-decl-tog-tabule}} (no longer in existence) ]=]-- local m_links = require("Module:tígunga") local strutils = require("Module:string utilities") local m_table = require("Module:tabule") local lang = require("Module:sprǣca").getByCode("ang") local u = mw.ustring.char local rsubn = mw.ustring.gsub local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar, n) local retval = rsubn(term, foo, bar, n) return retval end -- like str:gsub() but discards all but the first return value local function gsub(term, foo, bar, n) local retval = term:gsub(foo, bar, n) return retval end local export = {} local MACRON = u(0x0304) local LONG_VOWEL = "āēīōūȳǣ" .. MACRON -- No precomposed œ + macron local LONG_VOWEL_C = "[" .. LONG_VOWEL .. "]" local SHORT_VOWEL = "aeiouyæœ" local SHORT_VOWEL_C = "[" .. SHORT_VOWEL .. "]" local VOWEL = LONG_VOWEL .. SHORT_VOWEL local VOWEL_C = "[" .. VOWEL .. "]" local CONS_C = "[^" .. VOWEL .. "]" local cases = { "nom", "acc", "gen", "dat", "ins" } local numbers = { "sg", "pl" } local genders = { "m", "f", "n" } local slots = {} for _, case in ipairs(cases) do for _, number in ipairs(numbers) do local accel_number = number == "sg" and "s" or "p" for _, gender in ipairs(genders) do slots[case .. "_" .. number .. "_" .. gender] = case .. "|" .. gender .. "|" .. accel_number end end end local diphthongs = { ["ea"] = true, ["ēa"] = true, ["eā"] = true, ["eo"] = true, ["ēo"] = true, ["eō"] = true, ["io"] = true, ["īo"] = true, ["iō"] = true, ["ie"] = true, ["īe"] = true, ["iē"] = true, } local short_to_long_vowel = { ["a"] = "ā", ["e"] = "ē", ["i"] = "ī", ["o"] = "ō", ["u"] = "ū", ["y"] = "ȳ", ["æ"] = "ǣ", ["œ"] = "œ̄", -- the long vowel is two chars } local end_syllable_ok_list = { "ġn", "ġl", "sp", "st", "sc", "sċ", "sk", "ps", "ts", "cs", "þs", "ðs", "fs", "ks", "pt", "ct", "ft" } local end_syllable_ok = require("Module:table").listToSet(end_syllable_ok_list) -- This is used to determine whether a sequence of consonants XYZ is ok. -- If X == Y, or end_syllable_ok[XY], or sonority_classes[X] > sonority_classes[Y], -- we consider this OK (although when X == Y, the sequence XY reduces to a single -- consonant before another consonant). If a sesquence of consonants XYZ is OK, -- contraction of a vowel in the sequence XYVZ is possible (e.g. in adjective and -- verb forms), otherwise not. local sonority_classes = { ["b"] = 1, ["c"] = 1, ["ċ"] = 1, ["d"] = 1, ["f"] = 1, ["g"] = 1, ["ġ"] = 1, ["k"] = 1, ["p"] = 1, ["t"] = 1, ["þ"] = 1, ["ð"] = 1, ["x"] = 1, ["z"] = 1, ["h"] = 2, ["n"] = 3, ["m"] = 3, ["l"] = 4, ["r"] = 5, ["w"] = 6, ["ƿ"] = 6, } local function contraction_possible(x, y) -- If X or Y is unknown, treat contraction as not possible. return x == y or end_syllable_ok[x .. y] or (sonority_classes[x] or 0) > (sonority_classes[y] or 100) end local function break_vowels(vowelseq) local chars = rsplit(vowelseq, "") local vowels = {} local i = 1 while i <= #chars do if i < #chars and diphthongs[chars[i] .. chars[i + 1]] then table.insert(vowels, chars[i] .. chars[i + 1]) i = i + 2 else table.insert(vowels, chars[i]) i = i + 1 end end return vowels end -- Break a word into "syllables" where all syllables but the last one -- consist of zero or more consonants + a single vowel or dipthong, -- and the last "syllable" contains zero or more consonants without -- a vowel, representing any final consonants. (Even if there are no -- final consonants, there will always be a last vowelless syllable, -- in that case consisting of the empty string.) local function break_into_syllables(word) local vowel_cons = strutils.capturing_split(word, "(" .. VOWEL_C .. "+)") local syllables = {} for i = 1, #vowel_cons do if i % 2 == 1 then table.insert(syllables, vowel_cons[i]) else local vowels = break_vowels(vowel_cons[i]) for j = 1, #vowels do if j == 1 then syllables[#syllables] = syllables[#syllables] .. vowels[j] else table.insert(syllables, vowels[j]) end end end end return syllables end local function is_long(syllable, next_syllable) if rfind(syllable, LONG_VOWEL_C) or rfind(next_syllable, "^" .. CONS_C .. CONS_C) or rfind(next_syllable, "^x") then return true else return false end end local function lengthen_final_vowel(stem) local prefix, vowel = rmatch(stem, "^(.-)([eē][ao])$") if prefix then return prefix .. gsub(vowel, "^e", "ē") end prefix, vowel = rmatch(stem, "^(.-)([iī][eo])$") if prefix then return prefix .. gsub(vowel, "^i", "ī") end prefix, vowel = rmatch(stem, "^(.-)(" .. SHORT_VOWEL_C .. ")$") if prefix then return prefix .. short_to_long_vowel[vowel] end return stem end local function unpalatalize_final_c(stem) if type(stem) == "table" then local ret = {} for _, s in ipairs(stem) do table.insert(ret, unpalatalize_final_c(s)) end return ret end if rfind(stem, "ċċ$") then return rsub(stem, "ċċ$", "cc") elseif rfind(stem, "sċ$") then return stem else return rsub(stem, "ċ$", "c") end end local function make_table(args) local table_args = {title = args.title} local num = args.num local accel_lemma = args["lemma"] or args["nom_sg_m"] and args["nom_sg_m"][1] or nil local weakness = args["type"] == "strong" and "str|" or args["type"] == "weak" and "wk|" or "" for slot, accel_form in pairs(slots) do local table_arg = {} local forms = args[slot] or {"—"} for _, form in ipairs(forms) do table.insert(table_arg, form == "—" and form or m_links.full_link{ lang = lang, term = form, accel = { form = weakness .. accel_form, lemma = accel_lemma, } }) table_args[slot] = table.concat(table_arg, ", ") end end local sgtable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=1 | {nom_sg_m} | data-accel-col=2 | {nom_sg_f} | data-accel-col=3 | {nom_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=1 | {acc_sg_m} | data-accel-col=2 | {acc_sg_f} | data-accel-col=3 | {acc_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=1 | {gen_sg_m} | data-accel-col=2 | {gen_sg_f} | data-accel-col=3 | {gen_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=1 | {dat_sg_m} | data-accel-col=2 | {dat_sg_f} | data-accel-col=3 | {dat_sg_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=1 | {ins_sg_m} | data-accel-col=2 | {ins_sg_f} | data-accel-col=3 | {ins_sg_n} ]=] local pltable = [=[! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[masculine|Werlic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[feminine|Wīflic]] ! style="background: var(--wikt-palette-blue-0); font-size: 90%;"|[[neuter|Nāhwæðer]] |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[nominative case|Nemniendlic]] | data-accel-col=4 | {nom_pl_m} | data-accel-col=5 | {nom_pl_f} | data-accel-col=6 | {nom_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[accusative case|Wrēgendlic]] | data-accel-col=4 | {acc_pl_m} | data-accel-col=5 | {acc_pl_f} | data-accel-col=6 | {acc_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[genitive case|Āgniendlic]] | data-accel-col=4 | {gen_pl_m} | data-accel-col=5 | {gen_pl_f} | data-accel-col=6 | {gen_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[dative case|Forgifendlic]] | data-accel-col=4 | {dat_pl_m} | data-accel-col=5 | {dat_pl_f} | data-accel-col=6 | {dat_pl_n} |- ! style="background: var(--wikt-palette-blue-0); text-align: left; font-size: 90%;"|[[instrumental case|Tōllic]] | data-accel-col=4 | {ins_pl_m} | data-accel-col=5 | {ins_pl_f} | data-accel-col=6 | {ins_pl_n} ]=] local table_header = [=[<div class="NavFrame" style="max-width: 50em;"> <div class="NavHead" style="background:var(--wikt-palette-lighterblue,#EFF7FF)" >Declīnung of {title}</div> <div class="NavContent"> {\op}| style="background: var(--wikt-palette-grey-0,#F9F9F9); text-align:center; width:100%; line-height: 125%; border: 1px solid var(--wikt-palette-indigo-4,#CCCCFF);" cellpadding="3" cellspacing="1" class="inflection-table" ]=] local table_footer = [=[|{\cl}</div></div>]=] local table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}|- ! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local sg_table = [=[{table_header}! style="width: 16%; background: var(--wikt-palette-blue-0);" |[[singular|Ānfeald]] {sgtable}{table_footer}]=] local pl_table = [=[{table_header}! style="background: var(--wikt-palette-blue-0);" |[[plural|Manigfeald]] {pltable}{table_footer}]=] local formatted_table = strutils.format(num == "sg" and sg_table or num == "pl" and pl_table or table, {table_header = table_header, table_footer = table_footer, sgtable = sgtable, pltable = pltable}) return strutils.format(formatted_table, table_args) end local function make_table_with_overrides(genargs, lemma, userargs, userpref) genargs.lemma = lemma for slot, _ in pairs(slots) do if userargs[userpref .. slot] then genargs[slot] = rsplit(userargs[userpref .. slot], ", *") end end genargs.num = userargs.num return make_table(genargs) end local function compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) local args = {} local function add_ending(slot, stems, ending, construct_stem_ending) if not construct_stem_ending then function construct_stem_ending(stem, _) if ending == "-" then return stem end return stem .. ending end end if type(stems) ~= "table" then stems = {stems} end if not args[slot] then args[slot] = {} end for _, stem in ipairs(stems) do local form = construct_stem_ending(stem, ending) if type(form) == "string" then m_table.insertIfNot(args[slot], form) else for _, f in ipairs(form) do m_table.insertIfNot(args[slot], f) end end end end local function compute_stem_for_ending(ending) if ending == "" then return bare elseif ending:find("^n") then return cstemn elseif ending:find("^r") then return cstemr else return vstem end end local function construct_h_stem_ending(stem, ending) -- Delete a vowel at the beginning of the ending. But -um can -- either assume the contracted form (-m) or the full form (-um). if ending == "um" then return {stem .. "m", stem .. "um"} elseif ending:find("^[aeiou%-]") then return stem .. gsub(ending, "^.", "") else return stem .. ending end end local construct_stem_ending if h then construct_stem_ending = construct_h_stem_ending end local function add_row(slot_suffix, nom, acc, gen, dat, ins) local function add(slot_prefix, ending) if type(ending) ~= "table" then ending = {ending} end for _, e in ipairs(ending) do local stem = compute_stem_for_ending(e) add_ending(slot_prefix .. "_" .. slot_suffix, stem, e, construct_stem_ending) end end add("nom", nom) add("acc", acc) add("gen", gen) add("dat", dat) add("ins", ins) end if adjtype == "strong" then -- short == "h" is a special signal for adjectives in -h (esp. sċeolh, þweorh); -- use the vowel stem but have no ending. local short_ending = short == "opt" and {"", "u", "o"} or short == "h" and "-" or short and {"u", "o"} or "" add_row("sg_m", "", "ne", "es", "um", "e") add_row("sg_f", short_ending, "e", "re", "re", "re") add_row("sg_n", "", "", "es", "um", "e") add_row("pl_m", "e", "e", "ra", "um", "um") add_row("pl_f", {"a", "e"}, {"a", "e"}, "ra", "um", "um") add_row("pl_n", short_ending, short_ending, "ra", "um", "um") elseif adjtype == "weak" then add_row("sg_m", "a", "an", "an", "an", "an") add_row("sg_f", "e", "an", "an", "an", "an") add_row("sg_n", "e", "e", "an", "an", "an") add_row("pl_m", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_f", "an", "an", {"ra", "ena"}, "um", "um") add_row("pl_n", "an", "an", {"ra", "ena"}, "um", "um") else error("Unrecognized adjective type: '" .. adjtype .. "'") end return args end function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {required = true, default = "glæd"}, ["stem"] = {}, ["bare"] = {}, ["vstem"] = {}, ["cstem"] = {}, ["cstemn"] = {}, ["cstemr"] = {}, ["short"] = {}, ["contractable"] = {}, ["type"] = {}, ["h"] = {}, ["num"] = {}, } if parent_args["type"] == "strong" or parent_args["type"] == "weak" then for slot, _ in pairs(slots) do params[slot] = {} end else for slot, _ in pairs(slots) do params["str_" .. slot] = {} params["wk_" .. slot] = {} end end local args = require("Module:parameters").process(parent_args, params) local lemma = args[1] local stem, bare, vstem, cstemn, cstemr local short = false local contractable = false local adjtype = "normal" local h = false if rfind(lemma, CONS_C .. "a$") then adjtype = "weak" stem = args.stem or gsub(lemma, "a$", "") bare = stem else bare = lemma if rfind(lemma, CONS_C .. "e$") then short = true stem = args.stem or gsub(lemma, "e$", "") elseif rfind(lemma, CONS_C .. "[ou]$") then if lemma:find("o$") then bare = {lemma, gsub(lemma, "o$", "u")} else bare = {lemma, gsub(lemma, "u$", "o")} end stem = args.stem or gsub(lemma, ".$", "w") else stem = args.stem or lemma local syllables = break_into_syllables(stem) if #syllables == 1 then error("No vowels in stem: '" .. stem .. "'") end if rfind(stem, VOWEL_C .. ".*l[iī][cċ]$") then short = "opt" elseif is_long(syllables[#syllables - 1], syllables[#syllables]) or #syllables > 2 and not is_long(syllables[#syllables - 1], syllables[#syllables]) and not is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then -- "long stem", no -u in fem. sg. else short = true end if #syllables > 2 and --Special-case final -isċ and -iġ, which are contractable; --otherwise, should end in [eou] + single consonant, and not -ed. --In addition, the preceding syllable should be long. (rfind(stem, "is[cċ]$") or rfind(stem, "i[gġ]$") or (rfind(stem, CONS_C .. "[eou]" .. CONS_C .. "$") and not rfind(stem, "ed$"))) and is_long(syllables[#syllables - 2], syllables[#syllables - 1]) then local x, y = rmatch(syllables[#syllables - 1], "^" .. CONS_C .. "*(" .. CONS_C .. ")(" .. CONS_C .. ")") if not x or contraction_possible(x, y) then contractable = true end end end end if args.contractable then contractable = require("Module:yesno")(args.contractable) end if rfind(stem, "(" .. CONS_C .. ")%1$") then -- þicce, þynne, wann, ierre, etc. cstemn = rsub(stem, ".$", "") cstemr = cstemn elseif rfind(stem, CONS_C .. "r$") then -- ġīfre cstemn = gsub(stem, "r$", "er") cstemr = gsub(stem, "r$", "") elseif stem:find("[lr]n$") then -- dyrne cstemn = gsub(stem, "n$", "") cstemr = stem elseif rfind(stem, CONS_C .. "n$") then -- fǣcne cstemn = gsub(stem, "n$", "") cstemr = gsub(stem, "n$", "en") elseif rfind(stem, CONS_C .. "[lm]$") and not stem:find("[rl]m$") and not stem:find("rl$") then -- ǣ-cnōsle cstemn = gsub(stem, "(.)$", "e%1") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "h$") then -- hēah, wōh h = true short = "h" vstem = lengthen_final_vowel(gsub(stem, "h$", "")) cstemn = {vstem, vstem .. "n"} cstemr = {vstem, vstem .. "r"} elseif rfind(stem, "[lr]h$") then -- sċeolh, þweorh short = "h" local prefix, final_cons = rmatch(stem, "^(.-)([lr])h$") vstem = lengthen_final_vowel(prefix) .. final_cons cstemn = vstem cstemr = vstem elseif rfind(stem, CONS_C .. "w$") then -- nearu, stem nearw-, cstem nearo- cstemn = rsub(stem, "(.)w$", "%1o") cstemr = cstemn elseif rfind(stem, VOWEL_C .. "$") then -- frēo h = true short = "h" vstem = lengthen_final_vowel(stem) cstemn = vstem cstemr = vstem else cstemn = stem cstemr = stem end cstemn = unpalatalize_final_c(cstemn) cstemr = unpalatalize_final_c(cstemr) if vstem then -- already set for vowel-final and h-final stems elseif contractable then local beginning, c1, v, c2 = rmatch(stem, "(.-)(" .. CONS_C .. "*)(" .. VOWEL_C .. "+)(" .. CONS_C .. "+)$") if not c1 then error("Stem '" .. stem .. "' isn't contractable") end local contracted_c1 = c1 if rfind(contracted_c1, "(.)%1$") then contracted_c1 = rsub(contracted_c1, ".$", "") end vstem = {beginning .. c1 .. v .. c2, beginning .. unpalatalize_final_c(contracted_c1) .. c2} elseif rfind(stem, "æ" .. CONS_C .. "$") then vstem = rsub(stem, "æ(" .. CONS_C .. ")$", "a%1") elseif rfind(stem, "ea" .. CONS_C .. "$") then vstem = rsub(stem, "ea(" .. CONS_C .. ")$", "a%1") else vstem = stem end if args.short == "opt" then short = "opt" elseif args.short then short = require("Module:yesno")(args.short) end if args.h then h = require("Module:yesno")(args.h) end if args["type"] then adjtype = args["type"] end if args.bare then bare = rsplit(args.bare, ", *") end if args.vstem then vstem = rsplit(args.vstem, ", *") end if args.cstem then cstemn = rsplit(args.cstem, ", *") cstemr = cstemn end if args.cstemn then cstemn = rsplit(args.cstemn, ", *") end if args.cstemr then cstemr = rsplit(args.cstemr, ", *") end local function make_title(title_type) if args.title then return args.title end return require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " &mdash; " .. title_type end if adjtype == "strong" or adjtype == "weak" then local forms = compute_adj_args(adjtype, bare, vstem, cstemn, cstemr, short, h) forms.title = make_title(adjtype == "strong" and "Strong only" or "Weak only") return make_table_with_overrides(forms, lemma, args, "") else local strong_forms = compute_adj_args("strong", bare, vstem, cstemn, cstemr, short, h) strong_forms.title = make_title("Strang") strong_forms["type"] = "strong" local strong_table = make_table_with_overrides(strong_forms, lemma, args, "str_") local weak_forms = compute_adj_args("weak", bare, vstem, cstemn, cstemr, short, h) weak_forms.title = make_title("Unstrang") weak_forms["type"] = "weak" local weak_table = make_table_with_overrides(weak_forms, lemma, args, "wk_") return strong_table .. weak_table end end function export.make_table(frame) local parent_args = frame:getParent().args local params = { ["title"] = {default = "—"}, ["type"] = {}, ["lemma"] = {}, } for slot, _ in pairs(slots) do params[slot] = {} end local args = require("Module:parameters").process(parent_args, params) for k, v in pairs(args) do if slots[k] then args[k] = rsplit(v, ", *") end end return make_table(args) end return export 144z6vy4x70dst8tls6b0c48mo773ac Module:tígunga 828 8031 54797 2026-09-26T02:25:23Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} --[=[ [[Unsupported titles]], pages with high memory usage, extraction modules and part-of-speech names are listed at [[Module:tígunga/data]]. Other modules used: [[Module:script utilities]] [[Module:scripts]] [[Module:sprǣca]] and its submodules [[Module:hād and getæl]] [[Module:bētan/folgian]] ]=] local anchors_module = "Module:anchors" local debug_track_module = "Module:bētan/folgian" local decorations_module = "Module:...' 54797 Scribunto text/plain local export = {} --[=[ [[Unsupported titles]], pages with high memory usage, extraction modules and part-of-speech names are listed at [[Module:tígunga/data]]. Other modules used: [[Module:script utilities]] [[Module:scripts]] [[Module:sprǣca]] and its submodules [[Module:hād and getæl]] [[Module:bētan/folgian]] ]=] local anchors_module = "Module:anchors" local debug_track_module = "Module:bētan/folgian" local decorations_module = "Module:decorations" local form_of_module = "Module:form of" local gender_and_number_module = "Module:hād and getæl" local languages_module = "Module:sprǣca" local load_module = "Module:load" local memoize_module = "Module:memoize" local pages_module = "Module:pages" local scripts_module = "Module:scripts" local script_utilities_module = "Module:script utilities" local string_encode_entities_module = "Module:string/encode entities" local string_utilities_module = "Module:string utilities" local table_module = "Module:tabule" local utilities_module = "Module:utilities" local concat = table.concat local find = string.find local get_current_title = mw.title.getCurrentTitle local insert = table.insert local ipairs = ipairs local match = string.match local new_title = mw.title.new local pairs = pairs local remove = table.remove local sub = string.sub local toNFC = mw.ustring.toNFC local tostring = tostring local type = type local unstrip = mw.text.unstrip local NAMESPACE = get_current_title().nsText local function anchor_encode(...) anchor_encode = require(memoize_module)(mw.uri.anchorEncode, true) return anchor_encode(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end -- Can't yet replace, as the [[Module:string utilities]] version no longer has automatic double-encoding prevention, which requires changes here to account for. local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function language_anchor(...) language_anchor = require(anchors_module).language_anchor return language_anchor(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function request_script(...) request_script = require(script_utilities_module).request_script return request_script(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local m_headword_data local function get_headword_data() m_headword_data = load_data("Module:headword/data") return m_headword_data end local function track(page, code) local tracking_page = "links/" .. page debug_track(tracking_page) if code then debug_track(tracking_page .. "/" .. code) end end local function field_non_empty(list, field) if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `termobj.%s`=%s, should be %s\"table\""):format( field, mw.dumpObject(list), field == "q" or field == "qq" and "\"string\" or " or "")) end return list[1] end --[=[ Add any decorations (left or right regular or accent qualifiers, labels or references) to an item. `text` is the item's text (to which to add the decorations) and `itemobj` is the object specifying the item's decorations, which should optionally contain: * left regular qualifiers in `q` (an array of strings or a single string); an empty array will be ignored; * right regular qualifiers in `qq` (an array of strings or a single string); an empty array will be ignored; * left accent qualifiers in `a` (an array of strings); an empty array will be ignored; * right accent qualifiers in `aa` (an array of strings); an empty array will be ignored; * left labels in `l` (an array of strings); an empty array will be ignored; * right labels in `ll` (an array of strings); an empty array will be ignored; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; an empty array will be ignored. `lang` is a language object and is required if any accent qualifiers or labels are given. ]=] local function add_text_decorations(text, itemobj, lang) local q = itemobj.q if type(q) == "string" then q = { q } end local qq = itemobj.qq if type(qq) == "string" then qq = { qq } end if field_non_empty(q, "q") or field_non_empty(qq, "qq") or field_non_empty(itemobj.a, "a") or field_non_empty(itemobj.aa, "aa") or field_non_empty(itemobj.l, "l") or field_non_empty(itemobj.ll, "ll") or field_non_empty(itemobj.refs, "refs") then text = format_decorations { lang = lang, text = text, q = q, qq = qq, a = itemobj.a, aa = itemobj.aa, l = itemobj.l, ll = itemobj.ll, refs = itemobj.refs, } end return text end local function selective_trim(...) -- Unconditionally trimmed charset. local always_trim = "\194\128-\194\159" .. -- U+0080-009F (C1 control characters) "\194\173" .. -- U+00AD (soft hyphen) "\226\128\170-\226\128\174" .. -- U+202A-202E (directionality formatting characters) "\226\129\166-\226\129\169" -- U+2066-2069 (directionality formatting characters) -- Standard trimmed charset. local standard_trim = "%s" .. -- (default whitespace charset) "\226\128\139-\226\128\141" .. -- U+200B-200D (zero-width spaces) always_trim -- If there are non-whitespace characters, trim all characters in `standard_trim`. -- Otherwise, only trim the characters in `always_trim`. selective_trim = function(text) if text == "" then return text end local trimmed = trim(text, standard_trim) if trimmed ~= "" then return trimmed end return trim(text, always_trim) end return selective_trim(...) end local function escape(text, str) local rep repeat text, rep = text:gsub("\\\\(\\*" .. str .. ")", "\5%1") until rep == 0 return (text:gsub("\\" .. str, "\6")) end local function unescape(text, str) return (text :gsub("\5", "\\") :gsub("\6", str)) end -- Remove bold, italics, soft hyphens, strip markers and HTML tags. local function remove_formatting(str) str = str :gsub("('*)'''(.-'*)'''", "%1%2") :gsub("('*)''(.-'*)''", "%1%2") :gsub("­", "") return (unstrip(str) :gsub("<[^<>]+>", "")) end --[==[ Split `text` on double slashes (taking account of escaping backslashes). Return a list of split items. ]==] function export.split_on_slashes(text) if text:find("\\", nil, true) then track("escaped", "split_on_slashes") end text = split(escape(text, "//"), "//", true) or {} for i, v in ipairs(text) do text[i] = unescape(v, "//") if v == "" then text[i] = false end end return text end --[==[ If `text` is a wikilink (i.e. in the form `<nowiki>[[foo]]</nowiki>` or `<nowiki>[[foo|bar]]</nowiki>`), return the link target and display text. If the wikilink is single-part, the display text will be the same as the link target. If the text is not in wikilink form, return {nil} for both link target and display text. ]==] function export.get_wikilink_parts(text) -- TODO: replace `allow_bad_target` with `allow_unsupported`, with support for links to unsupported titles, including escape sequences. if ( -- Filters out anything but "[[...]]" with no intermediate "[[" or "]]". not match(text, "^()%[%[") or -- Faster than sub(text, 1, 2) ~= "[[". find(text, "[[", 3, true) or find(text, "]]", 3, true) ~= #text - 1 ) then return nil, nil end local pipe = find(text, "|", 3, true) local title, display if pipe then title, display = sub(text, 3, pipe - 1), sub(text, pipe + 1, -3) else title = sub(text, 3, -3) display = title end return title, display end -- Does the work of export.get_fragment, but can be called directly to avoid unnecessary checks for embedded links. local function get_fragment(text) text = escape(text, "#") -- Replace numeric character references with the corresponding character (&#39; → '), -- as they contain #, which causes the numeric character reference to be -- misparsed (wa'a → wa&#39;a → pagename wa&, fragment 39;a). text = decode_entities(text) local target, fragment = text:match("^(.-)#(.+)$") target = target or text target = unescape(target, "#") fragment = fragment and unescape(fragment, "#") or nil return target, fragment end --[==[ Given a link target possibly containing a fragment (i.e. a pound sign and following anchor), return two values, the actual target (minus the fragment) and the fragment, or {nil} if there is no fragment. ]==] function export.get_fragment(text) if text:find("\\", nil, true) then track("escaped", "get_fragment") end -- If there are no embedded links, process input. local open = find(text, "[[", nil, true) if not open then return get_fragment(text) end local close = find(text, "]]", open + 2, true) if not close then return get_fragment(text) -- If there is one, but it's redundant (i.e. encloses everything with no pipe), remove and process. elseif open == 1 and close == #text - 1 and not find(text, "|", 3, true) then return get_fragment(sub(text, 3, -3)) end -- Otherwise, return the input. return text, nil end --[==[ Given a link target as passed to `full_link()`, get the actual page that the target refers to. This removes bold, italics, strip markets and HTML; calls `makeEntryName()` for the language in question; converts targets beginning with `*` to the Reconstruction namespace; and converts appendix-constructed languages to the Appendix namespace. Returns up to three values: # the actual page to link to, or {nil} to not link to anything; # how the target should be displayed as, if the user didn't explicitly specify any display text; generally the same as the original target, but minus any anti-asterisk !!; # the value `true` if the target had a backslash-escaped * in it (FIXME: explain this more clearly). FIXME: This should not be calling deprecated `makeEntryName()` and should always return the same number of values. ]==] function export.get_link_page_with_auto_display(target, lang, sc, plain) local orig_target = target if not target then return nil elseif target:find("\\", nil, true) then track("escaped", "get_link_page") end target = remove_formatting(target) if target:sub(1, 1) == ":" then track("initial colon") -- FIXME, the auto_display (second return value) should probably remove the colon return target:sub(2), orig_target end local prefix = target:match("^(.-):") -- Convert any escaped colons target = target:gsub("\\:", ":") if prefix then -- If this is an a link to another namespace or an interwiki link, ensure there's an initial colon and then -- return what we have (so that it works as a conventional link, and doesn't do anything weird like add the term -- to a category.) prefix = ulower(trim(prefix)) if prefix ~= "" and ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then return target, orig_target end end -- Check if the term is reconstructed and remove any asterisk. Also check for anti-asterisk (!!). -- Otherwise, handle the escapes. local reconstructed, escaped, anti_asterisk if not plain then target, reconstructed = target:gsub("^%*(.)", "%1") if reconstructed == 0 then target, anti_asterisk = target:gsub("^!!(.)", "%1") if anti_asterisk == 1 then -- Remove !! from original. FIXME! We do it this way because the call to remove_formatting() above -- may cause non-initial !! to be interpreted as anti-asterisks. We should surely move the -- remove_formatting() call later. orig_target = orig_target:gsub("^!!", "") end end end target, escaped = target:gsub("^(\\-)\\%*", "%1*") if not (sc and sc:getCode() ~= "None") then sc = lang:findBestScript(target) end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). if (not sc:hasCapitalization()) and sc:isTransliterated() and target:match("%^") then target = escape(target, "^") :gsub("%^", "") target = unescape(target, "^") end -- Get the entry name for the language. target = lang:makeEntryName(target, sc, reconstructed == 1 or lang:hasType("appendix-constructed")) -- If the link contains unexpanded template parameters, then don't create a link. if target:match("{{{.-}}}") then -- FIXME: Should we return the original target as the default display value (second return value)? return nil end -- Link to appendix for reconstructed terms and terms in appendix-only languages. Plain links interpret * -- literally, however. if reconstructed == 1 then if lang:getFullCode() == "und" then -- Return the original target as default display value. If we don't do this, we wrongly get -- [Term?] displayed instead. return nil, orig_target end target = "Reconstruction:" .. lang:getFullName() .. "/" .. target -- Reconstructed languages and substrates require an initial *. elseif anti_asterisk ~= 1 and (lang:hasType("reconstructed") or lang:getFamilyCode() == "qfa-sub") then error(("The specified language %s is unattested, while the term '%s' does not begin with '*' to indicate that it is reconstructed.") : format(lang:getCanonicalName(), orig_target)) elseif lang:hasType("appendix-constructed") then target = "Appendix:" .. lang:getFullName() .. "/" .. target else target = target end return target, orig_target, escaped > 0 end function export.get_link_page(target, lang, sc, plain) local target, auto_display, escaped = export.get_link_page_with_auto_display(target, lang, sc, plain) return target, escaped end -- Make a link from a given link's parts. local function make_link(link, lang, sc, id, isolated, cats, no_alt_ast, plain) -- Convert percent encoding to plaintext. link.target = link.target and decode_uri(link.target, "PATH") link.fragment = link.fragment and decode_uri(link.fragment, "PATH") -- Find fragments (if one isn't already set). -- Prevents {{l|en|word#Etymology 2|word}} from linking to [[word#Etymology 2#English]]. -- # can be escaped as \#. if link.target and link.fragment == nil then link.target, link.fragment = get_fragment(link.target) end -- Process the target local auto_display, escaped link.target, auto_display, escaped = export.get_link_page_with_auto_display(link.target, lang, sc, plain) -- Create a default display form. -- If the target is "" then it's a link like [[#English]], which refers to the current page. if auto_display == "" then auto_display = (m_headword_data or get_headword_data()).pagename end -- If the display is the target and the reconstruction * has been escaped, remove the escaping backslash. if escaped then auto_display = auto_display:gsub("\\([^\\]*%*)", "%1", 1) end -- Process the display form. if link.display then local orig_display = link.display link.display = lang:makeDisplayText(link.display, sc, true) if cats then auto_display = lang:makeDisplayText(auto_display, sc) -- If the alt text is the same as what would have been automatically generated, then the alt parameter is -- redundant (e.g. {{l|en|foo|foo}}, {{l|en|w:foo|foo}}, but not {{l|en|w:foo|w:foo}}). If they're -- different, but the alt text could have been entered as the term parameter without it affecting the target -- page, then the target parameter is redundant (e.g. {{l|ru|фу|фу́}}). If `no_alt_ast` is true, use pcall -- to catch the error which will be thrown if this is a reconstructed lang and the alt text doesn't have *. if link.display == auto_display then insert(cats, lang:getFullName() .. " links with redundant alt parameters") else local ok, check if no_alt_ast then ok, check = pcall(export.get_link_page, orig_display, lang, sc, plain) else ok = true check = export.get_link_page(orig_display, lang, sc, plain) end if ok and link.target == check then insert(cats, lang:getFullName() .. " links with redundant target parameters") end end end else link.display = lang:makeDisplayText(auto_display, sc) end if not link.target then return link.display end -- If the target is the same as the current page, there is no sense id -- and either the language code is "und" or the current L2 is the current -- language then return a "self-link" like the software does. if link.target == get_current_title().prefixedText then local fragment, current_L2 = link.fragment, get_current_L2() if ( fragment and fragment == current_L2 or not (id or fragment) and (lang:getFullCode() == "und" or lang:getFullName() == current_L2) ) then return tostring(mw.html.create("strong") :addClass("selflink") :wikitext(link.display)) end end -- Add fragment. Do not add a section link to "Undetermined", as such sections do not exist and are invalid. -- TabbedLanguages handles links without a section by linking to the "last visited" section, but adding -- "Undetermined" would break that feature. For localized prefixes that make syntax error, please use the -- format: ["xyz"] = true. local prefix = link.target:match("^:*([^:]+):") prefix = prefix and ulower(prefix) if prefix ~= "category" and not (prefix and load_data("Module:data/interwikis")[prefix]) then if (link.fragment or link.target:sub(-1) == "#") and not plain then track("fragment", lang:getFullCode()) if cats then insert(cats, lang:getFullName() .. " links with manual fragments") end end if not link.fragment then if id then link.fragment = lang:getFullCode() == "und" and anchor_encode(id) or language_anchor(lang, id) elseif lang:getFullCode() ~= "und" and not (link.target:match("^Appendix:") or link.target:match("^Reconstruction:")) then link.fragment = anchor_encode(lang:getFullName()) end end end -- Put inward-facing square brackets around a link to isolated spacing character(s). if isolated and link.display[1] and not umatch(decode_entities(link.display), "%S") then link.display = "&#x5D;" .. link.display .. "&#x5B;" end link.target = link.target:gsub("^(:?)(.*)", function(m1, m2) return m1 .. encode_entities(m2, "#%&+/:<=>@[\\]_{|}") end) link.fragment = link.fragment and encode_entities(remove_formatting(link.fragment), "#%&+/:<=>@[\\]_{|}") return "[[" .. link.target:gsub("^[^:]", ":%0") .. (link.fragment and "#" .. link.fragment or "") .. "|" .. link.display .. "]]" end -- Split a link into its parts. local function parse_link(linktext) local link = { target = linktext } local target = link.target link.target, link.display = target:match("^(..-)|(.+)$") if not link.target then link.target = target link.display = target end -- There's no point in processing these, as they aren't real links. local target_lower = link.target:lower() for _, false_positive in ipairs({ "category", "cat", "file", "image" }) do if target_lower:match("^" .. false_positive .. ":") then return nil end end link.display = decode_entities(link.display) link.target, link.fragment = get_fragment(link.target) -- So that make_link does not look for a fragment again. if not link.fragment then link.fragment = false end return link end local function check_params_ignored_when_embedded(alt, lang, id, cats) if alt then track("alt-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored alt parameters") end end if id then track("id-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored id parameters") end end end -- Find embedded links and ensure they link to the correct section. local function process_embedded_links(text, alt, lang, sc, id, cats, no_alt_ast, plain) -- Process the non-linked text. text = lang:makeDisplayText(text, sc, true) -- If the text begins with * and another character, then act as if each link begins with *. However, don't do this if the * is contained within a link at the start. E.g. `|*[[foo]]` would set all_reconstructed to true, while `|[[*foo]]` would not. local all_reconstructed = false if not plain then -- anchor_encode removes links etc. if anchor_encode(text):sub(1, 1) == "*" then all_reconstructed = true end -- Otherwise, handle any escapes. text = text:gsub("^(\\-)\\%*", "%1*") end check_params_ignored_when_embedded(alt, lang, id, cats) local function process_link(space1, linktext, space2) local capture = "[[" .. linktext .. "]]" local link = parse_link(linktext) -- Return unprocessed false positives untouched (e.g. categories). if not link then return capture end if all_reconstructed then if link.target:find("^!!") then -- Check for anti-asterisk !! at the beginning of a target, indicating that a reconstructed term -- wants a part of the term to link to a non-reconstructed term, e.g. Old English -- {{ang-noun|m|head=*[[!!Crist|Cristes]] [[!!mæsseǣfen]]}}. link.target = link.target:sub(3) -- Also remove !! from the display, which may have been copied from the target (as in mæsseǣfen in -- the example above). link.display = link.display:gsub("^!!", "") elseif not link.target:match("^%*") then link.target = "*" .. link.target end end linktext = make_link(link, lang, sc, id, false, nil, no_alt_ast, plain) :gsub("^%[%[", "\3") :gsub("%]%]$", "\4") return space1 .. linktext .. space2 end -- Use chars 1 and 2 as temporary substitutions, so that we can use charsets. These are converted to chars 3 and 4 by process_link, which means we can convert any remaining chars 1 and 2 back to square brackets (i.e. those not part of a link). text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- If the script uses ^ to capitalize transliterations, make sure that any carets preceding links are on the inside, so that they get processed with the following text. if ( text:find("^", nil, true) and not sc:hasCapitalization() and sc:isTransliterated() ) then text = escape(text, "^") :gsub("%^\1", "\1%^") text = unescape(text, "^") end text = text:gsub("\1(%s*)([^\1\2]-)(%s*)\2", process_link) -- Remove the extra * at the beginning of a language link if it's immediately followed by a link whose display begins with * too. if all_reconstructed then text = text:gsub("^%*\3([^|\1-\4]+)|%*", "\3%1|*") end return (text :gsub("[\1\3]", "[[") :gsub("[\2\4]", "]]") ) end local function simple_link(term, fragment, alt, lang, sc, id, cats, no_alt_ast, suppress_redundant_wikilink_cat) local plain if lang == nil then lang, plain = get_lang("und"), true end -- Get the link target and display text. If the term is the empty string, treat the input as a link to the current page. if term == "" then term = get_current_title().prefixedText elseif term then local new_term, new_alt = export.get_wikilink_parts(term) if new_term then check_params_ignored_when_embedded(alt, lang, id, cats) -- [[|foo]] links are treated as plaintext "[[|foo]]". -- FIXME: Pipes should be handled via a proper escape sequence, as they can occur in unsupported titles. if new_term == "" then term, alt = nil, term else local title = new_title(new_term) if title then local ns = title.namespace -- File: and Category: links should be returned as-is. if ns == 6 or ns == 14 then return term end end term, alt = new_term, new_alt if cats then if not (suppress_redundant_wikilink_cat and suppress_redundant_wikilink_cat(term, alt)) then insert(cats, lang:getFullName() .. " links with redundant wikilinks") end end end end end if alt then alt = selective_trim(alt) if alt == "" then alt = nil end end -- If there's nothing to process, return nil. if not (term or alt) then return nil end -- If there is no script, get one. if not sc then sc = lang:findBestScript(alt or term) end -- Embedded wikilinks need to be processed individually. if term then local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, alt, lang, sc, id, cats, no_alt_ast, plain) end term = selective_trim(term) end -- If not, make a link using the parameters. return make_link({ target = term, display = alt, fragment = fragment }, lang, sc, id, true, cats, no_alt_ast, plain) end --[==[ Create a basic link to the given term. It links to the language section (such as `==English==`), but it does not add language and script wrappers, so any code that uses this function should call `[[Module:script utilities#tag_text]]` to add such wrappers itself at some point. The first argument, `data`, may contain the following items, a subset of the items used in the `data` argument of `##full_link`. If any other items are included, they are ignored. { { term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", no_alt_ast = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, cats = nil or {}, -- NOTE: If given, will be side-effected to return categories to add the page to. } } Specifically: * `term`: Term to turn into a link. This is generally the name of a page, possibly with extra diacritics added (e.g. length marks in Latin or Old English terms, accents in Russian terms, vowel diacritics in Arabic terms, etc.), which are stripped to determine the actual pagename. The term can contain wikilinks already embedded in it. These are processed individually just like a single link would be. The `alt` argument is ignored in this case. * `alt`: The alternative display for the link, if different from the linked page. If this is {nil}, the `term` argument is used instead (much like regular wikilinks). If `term` contains wikilinks in it, this argument is ignored and has no effect. (Links in which the alt is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/alt-ignored}}.) * `lang` ('''required'''): The [[Module:languages#Language objects|language object]] for the term being linked. The link or links in `term` will normally have their fragment set to point to the canonical name (see {{tl|language data documentation}}) of `lang` (or, if it is an etymology-only language, to the canonical name of its L2 parent). (However, if `id` is defined, the fragment will point to a language-specific sense ID corresponding to this field, which in turn will be overridden by `fragment` if specified.) * `sc`: The [[Module:scripts#Script objects|script object]] for the term being linked. This rarely needs to be specified because it is autodetected based on `term` or `alt`, and the detection is usually correct. It is used to determine how to convert the term into a pagename, possibly by stripping certain diacritics from the term's text. * `fragment`: If specified, overrides the fragment in the generated link (i.e. the portion after `#`, which determines where on the page to go to when the link is clicked). If not specified, the fragment is generated from `id` (if given) or otherwise from `lang`. * `id`: Sense ID string. If this argument is defined, the link will point to a language-specific sense ID ({{ll|en|identifier|id=HTML}}) created by the template {{temp|senseid}}. The fragment for a sense ID consists of the language's canonical name, a hyphen (`-`), and the string that was supplied as the `id` argument. This is useful when a term has more than one sense in a language. If the `term` argument contains wikilinks, this argument is ignored. (Links in which the sense ID is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/id-ignored}}.) * `no_alt_ast`: This is the same as `no_alt_ast` in `##full_link()`. See that function for more information. * `suppress_redundant_wikilink_cat`: This is the same as `suppress_redundant_wikilink_cat` in `##full_link()`. See that function for more information. * `cats`: This should be either {nil} or an empty list. In the latter case, tracking categories will be added to the list when appropriate. The caller can choose to add the page to those categories (as is done by ##full_link()`). The following special options are processed for each link (both simple terms and with embedded wikilinks): * The target page name will be processed by stripping certain diacritics (as mentioned above) and converting the resulting ''logical'' pagename to a ''physical'' pagename (which will be different from the logical pagename in the case of pages with unsupported characters in them and certain overly large pages, such as [[a]], that are split into parts). * If the term starts with `*`, then it is considered a reconstructed term, and a link to the `Reconstruction:` namespace will be created. If the text contains embedded wikilinks, then `*` is automatically applied to each one individually, while preserving the displayed form of each link as it was given. This allows linking to phrases containing multiple reconstructed terms, while only showing the `*` once at the beginning. * If the text starts with `:`, then the link is treated as "raw" and the above steps are skipped. This can be used in rare cases where the page name begins with `*` or if diacritics should not be stripped. For example: ** {{tl|l|en|*nix}} links to the nonexistent page [[Reconstruction:English/nix]] (`*` is interpreted as a reconstruction), but {{tl|l|en|:*nix}} links to [[*nix]]. ** {{tl|l|sl|Franche-Comté}} links to the nonexistent page [[Franche-Comte]] (`é` is converted to `e` by the diacritic-stripping process), but {{tl|l|sl|:Franche-Comté}} links to [[Franche-Comté]]. ]==] function export.language_link(data) if type(data) ~= "table" then error( "The first argument to the function language_link must be a table. See [[Module:links/documentation]] for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "language_link") end -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end return simple_link( data.term, data.fragment, data.alt, lang, data.sc, data.id, cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end function export.plain_link(data) if type(data) ~= "table" then error( "The first argument to the function plain_link must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "plain_link") end return simple_link( data.term, data.fragment, data.alt, nil, data.sc, data.id, data.cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end --[==[Replace any links with links to the correct section, but don't link the whole text if no embedded links are found. Returns the display text form.]==] function export.embedded_language_links(data) if type(data) ~= "table" then error( "The first argument to the function embedded_language_links must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "embedded_language_links") end local term, lang, sc = data.term, data.lang, data.sc -- If we don't have a script, get one. if not sc then sc = lang:findBestScript(term) end -- Do we have embedded wikilinks? If so, they need to be processed individually. local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, data.alt, lang, sc, data.id, data.cats, data.no_alt_ast) end -- If not, return the display text. term = selective_trim(term) -- FIXME: Double-escape any percent-signs, because we don't want to treat non-linked text as having percent-encoded -- characters. This is a hack: percent-decoding should come out of [[Module:languages]] and only dealt with in this -- module, as it's specific to links. term = term:gsub("%%", "%%25") return lang:makeDisplayText(term, sc, true) end function export.mark(text, item_type, face, lang) local tag = { "", "" } if item_type == "gloss" then tag = { '<span class="mention-gloss-double-quote">“</span><span class="mention-gloss">', '</span><span class="mention-gloss-double-quote">”</span>' } if type(text) == "string" and text:match("^''[^'].*''$") then -- Temporary tracking for mention glosses that are entirely italicized or bolded, which is probably -- wrong. (Note that this will also find bolded mention glosses since they use triple apostrophes.) track("italicized-mention-gloss", lang and lang:getFullCode() or nil) end elseif item_type == "tr" then if face == "term" then tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr mention-tr Latn">', '</span>' } else tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr Latn">', '</span>' } end elseif item_type == "ts" then -- \226\129\160 = word joiner (zero-width non-breaking space) U+2060 tag = { '<span class="ts mention-ts Latn">/\226\129\160', '\226\129\160/</span>' } elseif item_type == "pos" then tag = { '<span class="ann-pos">', '</span>' } elseif item_type == "non-gloss" then tag = { '<span class="ann-non-gloss">', '</span>' } elseif item_type == "annotations" then tag = { '<span class="mention-gloss-paren annotation-paren">(</span>', '<span class="mention-gloss-paren annotation-paren">)</span>' } elseif item_type == "infl" then tag = { '<span class="ann-infl">', '</span>' } end if type(text) == "string" then return tag[1] .. text .. tag[2] else return "" end end --[=[ Implementation of `format_transliteration` and `format_transcription`. The implementation is identical except that the field containing the transliteration or transcription may vary and is specified in `field`, and the way a given transliteration or transcription is tagged may vary and is controlled by `tag_fn`, which is passed three parameters: `text` (the transliteration or transcription), `lang` (the language passed in) and `face` (the face passed in). On input, `item` is the transliteration or transcription or list of such objects; `field` is either {"tr"} or {"ts"}; `tag_fn` is a function of three parameters to tag the item, as described above; `lang` is the language object of the term whose transliteration or transcription is specified; and `face` is a string indicating how to display the item (generally only the strings {"term"} and {"default"} are recognized). ]=] local function format_transliteration_or_transcription(item, field, tag_fn, lang, item_face) if type(item) == "string" then return tag_fn(item, lang, item_face) end local formatted_items = {} for _, itemobj in ipairs(item) do local tagged_item = tag_fn(itemobj[field], lang, item_face) tagged_item = add_text_decorations(tagged_item, item, lang) insert(formatted_items, tagged_item) end if formatted_items[2] then -- FIXME: This should be customizable. return concat(formatted_items, " <i>or</i> ") else return formatted_items[1] end end --[==[ Format a transliteration string or list of transliteration objects. `tr` contains the transliteration(s), which for forward compatibility reasons can only be either a single transliteration string or a list of transliteration objects (each of which has a `tr` field holding the transliteration and optional fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transliterations as well as decorations (qualifiers, labels or references) attached to transliterations. ]==] function export.format_transliteration(tr, lang, face) return format_transliteration_or_transcription(tr, "tr", tag_translit, lang, face) end local function tag_transcription(ts, _lang, _face) return export.mark(ts, "ts") end --[==[ Format a transcription string or list of transcription objects. `ts` contains the transcription(s), which for forward compatibility reasons can only be either a single transcription string or a list of transcription objects (each of which has a `ts` field holding the transcription and optional decoration fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transcriptions as well as decorations (qualifiers, labels or references) attached to transcriptions. ]==] function export.format_transcription(ts, lang, face) return format_transliteration_or_transcription(ts, "ts", tag_transcription, lang, face) end local pos_tags --[==[ Format the annotations that are displayed with a link created by `full_link()`. Annotations are the extra bits of information that are displayed following the linked term, and include things such as gender, transliteration, gloss, etc. The first argument is a table with some or all of the following keys (all are optional): * `interwiki`: An interwiki link. This is used for links in translation tables to the corresponding term in another Wiktionary. If specified, it should be a fully formatted link and is inserted as-is at the beginning of the output. See the `interwiki()` function in [[Module:translations]]. * `genders`: Table containing a list of gender specifications in the style of [[Module:gender and number]]. If specified, these are formatted using `format_genders()` in [[Module:gender and number]] and the result inserted at the beginning of the output, following any interwiki link and (in all cases) directly after a no-break space. * `tr`: Transliteration or transliterations. Currently, this is always a one-item list. It is a list because of potential support for per-alternant transliterations due to the multiple alternants (separated by `//`) that can be specified in `term` or `alt`. The item in the list can be either a string or a list of transliteration objects (see `format_transliteration()`). * `ts`: Transcription or transcriptions. Like `tr`, this is currently always a one-item list, with the item being either a single string or a list of transcription objects, as described in `format_transcription()`. * `gloss`: Gloss that translates the term in the link. * `pos`: Part of speech of the linked term. If the given argument matches one of the aliases in `pos_aliases` in [[Module:headword/data]], or consists of a part of speech or alias followed by `f` (for a non-lemma form), expand it appropriately. Otherwise, just show the given text as it is. * `infl`: A list of tags, each a string. Multiple tag sets may be encoded in this list by separating them with an element consisting of a semicolon. If there are multiple tag sets, they are formatted individually and separated by a semicolon + space. * `ng`: Arbitrary non-gloss descriptive text for the link. This should be used in preference to putting descriptive text in `gloss` or `pos`. * `lit`: Literal meaning of the term, if the usual meaning is figurative or idiomatic. * `postprocess_annotations`: A function to postprocess the annotations, after they have been formatted (see below). The `interwiki` and `genders` properties are formatted specially, and `postprocess_annotations` is a callback function rather than an item to display; all others are formatted (each in their own way), separated by commas and placed inside of parentheses (except that if both transliteration and transcription are present, they are separated by a space). The order of the annotations is `tr`+`ts`, `gloss`, `pos`, `infl`, `ng` and `lit`. The `postprocess_annotations` function, if supplied, is passed a single object, a table with two keys `data` (the `data` object passed into `format_link_annotations()`) and `annotations` (the formatted annotations, prior to being concatenated). It should side-effect the `annotations` list, e.g. by inserting more annotations. (It is used to handle nested inflections in [[Module:headword]]. FIXME: It should probably be generalized so that it can return the final formatted string, to allow for e.g. changing the way the annotations are formatted.) * The second argument is a string controlling the "face" that the terms are displayed in. Currently it only affects transliteration and transcription and only when the value {"term"} is passed in, in which case those annotations are displayed italicized. ]==] function export.format_link_annotations(data, face) local output = {} -- Interwiki link if data.interwiki then insert(output, data.interwiki) end -- Genders if type(data.genders) ~= "table" then data.genders = { data.genders } end if data.genders and data.genders[1] then local genders, gender_cats = format_genders(data.genders, data.lang) insert(output, "&nbsp;" .. genders) if gender_cats then local cats = data.cats if cats then extend(cats, gender_cats) end end end local annotations = {} -- Transliteration and transcription local tr = data.tr and data.tr[1] or nil local ts = data.ts and data.ts[1] or nil if tr or ts then local item_face if face == "term" then item_face = face else item_face = "default" end local formatted_tr = tr and export.format_transliteration(tr, data.lang, item_face) or nil local formatted_ts = ts and export.format_transcription(ts, data.lang, item_face) or nil if formatted_tr and formatted_ts then insert(annotations, formatted_tr .. " " .. formatted_ts) else insert(annotations, formatted_tr or formatted_ts) end end -- Gloss/translation if data.gloss then insert(annotations, export.mark(data.gloss, "gloss")) end -- Part of speech if data.pos then -- debug category for pos= containing transcriptions if data.pos:match("/[^><]-/") then data.pos = data.pos .. "[[Category:links likely containing transcriptions in pos]]" end -- Canonicalize part of speech aliases as well as non-lemma aliases like 'nf' or 'nounf' for "noun form". pos_tags = pos_tags or (m_headword_data or get_headword_data()).pos_aliases local pos = pos_tags[data.pos] if not pos and data.pos:find("f$") then local pos_form = data.pos:sub(1, -2) -- We only expand something ending in 'f' if the result is a recognized non-lemma POS. pos_form = (pos_tags[pos_form] or pos_form) .. " form" if (m_headword_data or get_headword_data()).nonlemmas[pos_form .. "s"] then pos = pos_form end end insert(annotations, export.mark(pos or data.pos, "pos")) end -- Inflection data if data.infl then local m_form_of = require(form_of_module) -- Split tag sets manually, since tagged_inflections creates a numbered list, and we do not want that. local infl_outputs = {} local tag_sets = m_form_of.split_tag_set(data.infl) for _, tag_set in ipairs(tag_sets) do table.insert(infl_outputs, m_form_of.tagged_inflections({ tags = tag_set, lang = data.lang, nocat = true, nolink = true, nowrap = true })) end insert(annotations, export.mark(table.concat(infl_outputs, "; "), "infl")) end -- Non-gloss text if data.ng then insert(annotations, export.mark(data.ng, "non-gloss")) end -- Literal/sum-of-parts meaning if data.lit then insert(annotations, "literally " .. export.mark(data.lit, "gloss")) end -- Provide a hook to insert additional annotations such as nested inflections. if data.postprocess_annotations then data.postprocess_annotations { data = data, annotations = annotations } end if annotations[1] then insert(output, " " .. export.mark(concat(annotations, ", "), "annotations")) end return concat(output) end -- Encode certain characters to avoid various delimiter-related issues at various stages. We need to encode < and > -- because they end up forming part of CSS class names inside of <span ...> and will interfere with finding the end -- of the HTML tag. I first tried converting them to URL encoding, i.e. %3C and %3E; they then appear in the URL as -- %253C and %253E, which get mapped back to %3C and %3E when passed to [[Module:accel]]. But mapping them to &lt; -- and &gt; somehow works magically without any further work; they appear in the URL as < and >, and get passed to -- [[Module:accel]] as < and >. I have no idea who along the chain of calls is doing the encoding and decoding. If -- someone knows, please modify this comment appropriately! local accel_char_map local function get_accel_char_map() accel_char_map = { ["%"] = ".", [" "] = "_", ["_"] = u(0xFFF0), ["<"] = "&lt;", [">"] = "&gt;", } return accel_char_map end local function encode_accel_param_chars(param) return (param:gsub("[%% <>_]", accel_char_map or get_accel_char_map())) end local function encode_accel_param(prefix, param) if not param then return "" end if type(param) == "table" then local filled_params = {} -- There may be gaps in the sequence, especially for translit params. local maxindex = 0 for k in pairs(param) do if type(k) == "number" and k > maxindex then maxindex = k end end for i = 1, maxindex do filled_params[i] = param[i] or "" end -- [[Module:accel]] splits these up again. param = concat(filled_params, "*~!") end -- This is decoded again by [[WT:ACCEL]]. return prefix .. encode_accel_param_chars(param) end local function insert_if_not_blank(list, item) if item == "" then return end insert(list, item) end local function get_css_classes(lang, tr, accel, nowrap) if not accel and not nowrap then return "" end local classes = {} if accel then insert(classes, "form-of lang-" .. lang:getFullCode()) local form = accel.form if form then insert(classes, encode_accel_param_chars(form) .. "-form-of") end insert_if_not_blank(classes, encode_accel_param("gender-", accel.gender)) insert_if_not_blank(classes, encode_accel_param("pos-", accel.pos)) insert_if_not_blank(classes, encode_accel_param("transliteration-", accel.translit or (tr ~= "-" and tr or nil))) insert_if_not_blank(classes, encode_accel_param("target-", accel.target)) insert_if_not_blank(classes, encode_accel_param("origin-", accel.lemma)) insert_if_not_blank(classes, encode_accel_param("origin_transliteration-", accel.lemma_translit)) if accel.no_store then insert(classes, "form-of-nostore") end end if nowrap then insert(classes, nowrap) end return concat(classes, " ") end --[==[ Creates a full link, with annotations (see `##format_link_annotations`), in the style of {{tl|l}} or {{tl|m}}. The first argument, `data`, must be a table. It contains the various elements that can be supplied as parameters to {{tl|l}} or {{tl|m}}: { { -- Basic link-related fields term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", accel = {accelerated_creation_tags}, -- Link annotation fields interwiki = "interwiki_link", genders = {"gender1", "gender2", ...} or {{spec = "gender1", q = {"left qualifier", ...}, qq = {"right qualifier"}, ...}, ...}, tr = "transliteration" or "-" or {{tr = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, ts = "transcription" or {{ts = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, gloss = "gloss", pos = "part_of_speech_tag", infl = {"infl1_tag1", "infl1_tag2", ..., ";", "infl2_tag1", "infl2_tag2", ...}, ng = "non-gloss text", lit = "literal_translation", postprocess_annotations = function({data = full_link_data, annotations = {"annotation1", "annotation2", ...}}) -> nil, -- Other transliteration-related fields respect_link_tr = boolean, never_call_transliteration_module = boolean, suppress_tr = boolean, -- Decoration fields q = { "left_qualifier1", "left_qualifier2", ...} or "left_qualifier", qq = { "right_qualifier1", "right_qualifier2", ...} or "right_qualifier", l = { "left_label1", "left_label2", ...}, ll = { "right_label1", "right_label2", ...}, a = { "left_accent_qualifier1", "left_accent_qualifier2", ...}, aa = { "right_accent_qualifier1", "right_accent_qualifier2", ...}, refs = { "formatted_ref1", "formatted_ref2", ...} or { {text = "text", name = "name", group = "group"}, ... }, pretext = "text_at_beginning", posttext = "text_at_end", show_decorations = boolean, -- Fields controlling tracking categories track_sc = boolean, no_nonstandard_sc_cat = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, -- Miscellaneous fields no_alt_ast = boolean, no_generate_alternants = boolean, } } Any one of the items in the `data` table (except for `lang`) may be {nil}. If none of `term`, `alt` and `tr` is present, a term request will be shown. Thus, calling {full_link{ term = term, lang = lang, sc = sc }}, where `term` is the page to link to (which may have diacritics that will be stripped and/or embedded bracketed links) and `lang` is a [[Module:languages#Language objects|language object]] from [[Module:languages]], will give a plain link similar to the one produced by the template {{tl|l}}, and calling {full_link( { term = term, lang = lang, sc = sc }, "term" )} will give a link similar to the one produced by the template {{tl|m}}. The function will: * Try to determine the script, based on the characters found in the `term` or `alt` argument, if the script was not given. If a script is given and `track_sc` is {true}, it will check whether the input script is the same as the one which would have been automatically generated and add the category ` ``lang`` terms with redundant script codes` if yes, or ` ``lang`` terms with non-redundant manual script codes` if no. This should be used when the input script object is directly determined by a template's `sc` parameter. * Call `simple_link()` on the `term` or `alt` forms, to remove diacritics in the page name, process any embedded wikilinks and create links to Reconstruction or Appendix pages when necessary. (`simple_link()` is almost exactly the same as `##language_link()`; the latter is a simple wrapper around the former that adds a bit of extra tracking.) * Call `[[Module:script utilities#tag_text]]` to add the appropriate language and script tags to the term and italicize terms written in the Latin script if necessary. Accelerated creation tags, as used by [[WT:ACCEL]], are included. * Generate a transliteration, based on the `alt` or `term` arguments, if the script is not Latin, no transliteration was provided in `tr` and the combination of the term's language and script support automatic transliteration. The transliteration itself will be linked if both `.respect_link_tr` is specified and the language of the term has the `link_tr` property set for the script of the term; but not otherwise. * Add the annotations (transliteration, gender, gloss, etc.) after the link. * If `no_alt_ast` is specified, then the `alt` text does not need to contain an asterisk if the language is reconstructed. This should only be used by modules which really need to allow links to reconstructions that don't display asterisks (e.g. number boxes). * If `suppress_redundant_wikilink_cat` is specified, it should be a function that indicates whether to suppress the generation of the ` ``lang`` links with redundant wikilinks` tracking category. It is passed two arguments, the `term` and `alt` parameters. Normally, this tracking category is added whenever the `term` argument consists entirely of a one-part or two-part embedded link, which is considered "redundant" in that the link can be rewritten into separate `term` and `alt` arguments without any embedded links. For certain wrapping templates, however, otherwise "redundant" embedded links are necessary to prevent interpretation of certain characters as delimiters. For example, {{tl|col}} and related templates use `~` as a separator, as well as `,` when not followed by a space. In these templates, embedded links are required to correctly link to terms containing those delimiters, such as [[Micros~1]] and [[1,6-Cleves acid]], but will incorrectly trigger the addition of the tracking category unless the appropriate `suppress_redundant_wikilink_cat` function is given. * If `pretext` or `posttext` is specified, this is text to (respectively) prepend or append to the output, directly before processing decorations (qualifiers, labels and references). This can be used to add arbitrary extra text inside of the decorations. * If `show_decorations` is specified, then decorations specified in `data` (i.e. left and right qualifiers, accent qualifiers, labels and references) will be displayed, otherwise they will be ignored. (This is because a fair amount of code stores decorations in these fields and displays them itself, rather than expecting {full_link()} to display them.) * ]==] function export.full_link(data, face, allow_self_link, show_qualifiers) if type(data) ~= "table" then error("The first argument to the function full_link must be a table. " .. "See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "full_link") end if show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-17, remove after 2026-10-17 or so. error("Can't pass fourth parameter `show_qualifiers` any more. Set `show_decorations = true` on data.") end if data.show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-18, remove after 2026-10-18 or so. error("Can't set field `show_qualifiers`; use `show_decorations`") end if data.no_generate_forms then -- FIXME: Eventually remove the error code. Added 2026-09-14, remove after 2026-10-14 or so. error("Can't set field `no_generate_forms`; use `no_generate_alternants`") end -- Prevent data from being destructively modified. data = shallow_copy(data) data.cats = {} -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end local terms = { true } -- Generate multiple alternants if applicable. for _, param in ipairs { "term", "alt" } do if type(data[param]) == "string" and data[param]:find("//", nil, true) then data[param] = export.split_on_slashes(data[param]) elseif type(data[param]) == "string" and not (type(data.term) == "string" and data.term:find("//", nil, true)) then if not data.no_generate_alternants then data[param] = lang:generateAlternants(data[param]) else data[param] = { data[param] } end else data[param] = {} end end for _, param in ipairs { "sc", "tr", "ts" } do data[param] = { data[param] } end for _, param in ipairs { "term", "alt", "sc", "tr", "ts" } do for i in pairs(data[param]) do terms[i] = true end end -- Create the link local outparts = {} local id, no_alt_ast, suppress_redundant_wikilink_cat, accel, never_call_transliteration_module = data.id, data.no_alt_ast, data.suppress_redundant_wikilink_cat, data.accel, data.never_call_transliteration_module local link_tr = data.respect_link_tr and lang:link_tr(data.sc[1]) for i in ipairs(terms) do local link -- Is there any text to show? if (data.term[i] or data.alt[i]) then -- Try to detect the script if it was not provided local display_term = data.alt[i] or data.term[i] local best = lang:findBestScript(display_term) -- no_nonstandard_sc_cat is intended for use in [[Module:interproject]] if ( not data.no_nonstandard_sc_cat and best:getCode() == "None" and find_best_script_without_lang(display_term):getCode() ~= "None" ) then insert(cats, lang:getFullName() .. " terms in nonstandard scripts") end if not data.sc[i] then data.sc[i] = best -- Track uses of sc parameter. elseif data.track_sc then if data.sc[i]:getCode() == best:getCode() then insert(cats, lang:getFullName() .. " terms with redundant script codes") else insert(cats, lang:getFullName() .. " terms with non-redundant manual script codes") end end -- If using a discouraged character sequence, add to maintenance category if data.sc[i]:hasNormalizationFixes() == true then if (data.term[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.term[i])) ~= toNFC(data.term[i])) or (data.alt[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.alt[i])) ~= toNFC(data.alt[i])) then insert(cats, "Pages using discouraged character sequences") end end link = simple_link( data.term[i], data.fragment, data.alt[i], lang, data.sc[i], id, cats, no_alt_ast, suppress_redundant_wikilink_cat ) end -- simple_link can return nil, so check if a link has been generated. if link then -- Add "nowrap" class to prefixes in order to prevent wrapping after the hyphen local nowrap local display_term = data.alt[i] or data.term[i] if display_term and (display_term:find("^%-") or display_term:find("^־")) then -- Hebrew maqqef -- FIXME, use hyphens from [[Module:affix]] nowrap = "nowrap" end link = tag_text(link, lang, data.sc[i], face, get_css_classes(lang, data.tr[i], accel, nowrap)) else --[[ No term to show. Is there at least a transliteration we can work from? ]] link = request_script(lang, data.sc[i]) -- No link to show, and no transliteration either. Show a term request (unless it's a substrate, as they rarely take terms). if (link == "" or (not data.tr[i]) or data.tr[i] == "-") and lang:getFamilyCode() ~= "qfa-sub" then -- If there are multiple terms, break the loop instead. if i > 1 then remove(outparts) break elseif NAMESPACE ~= "Template" then insert(cats, lang:getFullName() .. " term requests") end link = "<small>[Term?]</small>" end end insert(outparts, link) if i < #terms then insert(outparts, "<span class=\"Zsym mention\" style=\"font-size:100%;\">&nbsp;/ </span>") end end -- When suppress_tr is true, do not show or generate any transliteration if data.suppress_tr then data.tr[1] = nil else -- TODO: Currently only handles the first transliteration, pending consensus on how to handle multiple translits for multiple forms, as this is not always desirable (e.g. traditional/simplified Chinese). if data.tr[1] == "" or data.tr[1] == "-" then data.tr[1] = nil else local phonetic_extraction = load_data("Module:links/data").phonetic_extraction phonetic_extraction = phonetic_extraction[lang:getCode()] or phonetic_extraction[lang:getFullCode()] if phonetic_extraction then data.tr[1] = data.tr[1] or require(phonetic_extraction).getTranslit(export.remove_links(data.alt[1] or data.term[1])) elseif (data.term[1] or data.alt[1]) and data.sc[1]:isTransliterated() then -- Track whenever there is manual translit. The categories below like 'terms with redundant transliterations' -- aren't sufficient because they only work with reference to automatic translit and won't operate at all in -- languages without any automatic translit, like Persian and Hebrew. if data.tr[1] then local full_code = lang:getFullCode() track("manual-tr", full_code) end if not never_call_transliteration_module then -- Try to generate a transliteration. local text = data.alt[1] or data.term[1] if not link_tr then text = export.remove_links(text, true) end local automated_tr = lang:transliterate(text, data.sc[1]) if automated_tr then local manual_tr = data.tr[1] if manual_tr then if export.remove_links(manual_tr) == export.remove_links(automated_tr) then insert(cats, lang:getFullName() .. " terms with redundant transliterations") else -- Prevents Arabic root categories from flooding the tracking categories. if NAMESPACE ~= "Category" then insert(cats, lang:getFullName() .. " terms with non-redundant manual transliterations") end end end if not manual_tr or lang:overrideManualTranslit(data.sc[1]) then data.tr[1] = automated_tr end end end end end end -- Link to the transliteration entry for languages that require this if data.tr[1] and link_tr and not data.tr[1]:match("%[%[(.-)%]%]") then data.tr[1] = simple_link( data.tr[1], nil, nil, lang, get_script("Latn"), nil, cats, no_alt_ast, suppress_redundant_wikilink_cat ) elseif data.tr[1] and not link_tr then -- Remove the pseudo-HTML tags added by remove_links. data.tr[1] = data.tr[1]:gsub("</?link>", "") end if data.tr[1] and not umatch(data.tr[1], "[^%s%p]") then data.tr[1] = nil end insert(outparts, export.format_link_annotations(data, face)) if data.pretext then insert(outparts, 1, data.pretext) end if data.posttext then insert(outparts, data.posttext) end local categories = cats[1] and format_categories(cats, lang, "-", nil, nil, data.sc) or "" local output = concat(outparts) if data.show_decorations then output = add_text_decorations(output, data, lang) end return output .. categories end --[==[ Strip links by replacing all wikilinks with their displayed text, and remove any categories. This function can be invoked either from a template or from another module. Specifically, this function deletes category links, the targets of piped links, and any double square brackets involved in links (other than file links, which are untouched). If `tag` is set, then any links removed will be given pseudo-HTML tags, which allow the substitution functions in [[Module:languages]] to properly subdivide the text in order to reduce the chance of substitution failures in modules which scrape pages like [[Module:zh-translit]]. (FIXME: This is quite hacky. We probably want this to be integrated into [[Module:languages]], but we can't do that until we know that nothing is pushing pipe linked transliterations through it for languages which don't have link_tr set.) * `<nowiki>[[page|displayed text]]</nowiki>` &rarr; `displayed text` * `<nowiki>[[page and displayed text]]</nowiki>` &rarr; `page and displayed text` * `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` &rarr; ''(nothing)'' ]==] function export.remove_links(text, tag) if type(text) == "table" then text = text.args[1] end if not text or text == "" then return "" end text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- Parse internal links for the display text. text = text:gsub("(\1)([^\1\2]-)(\2)", function(c1, c2, c3) -- Don't remove files. for _, false_positive in ipairs({ "file", "image" }) do if c2:lower():match("^" .. false_positive .. ":") then return c1 .. c2 .. c3 end end -- Remove categories completely. for _, false_positive in ipairs({ "category", "cat" }) do if c2:lower():match("^" .. false_positive .. ":") then return "" end end -- In piped links, remove all text before the pipe, unless it's the final character (i.e. the pipe trick), in which case just remove the pipe. c2 = c2:match("^[^|]*|(.+)") or c2:match("([^|]+)|$") or c2 if tag then return "<link>" .. c2 .. "</link>" else return c2 end end) text = text :gsub("\1", "[[") :gsub("\2", "]]") return text end function export.section_link(link) if type(link) ~= "string" then error("The first argument to section_link was a " .. type(link) .. ", but it should be a string.") elseif link:find("\\", nil, true) then track("escaped", "section_link") end local target, section = get_fragment((link:gsub("_", " "))) if not section then error("No \"#\" delineating a section name") end return simple_link( target, section, target .. " §&nbsp;" .. section ) end return export ljji6z7ju8wst65fu52ab28w6x11nso link 0 8032 54798 2026-09-26T02:41:20Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈlɪŋk/ ===Wordstǣr=== Of Middelenglisce ''[[linke]]'' ===Nama=== '''link''' {{en-noun}} # þæt [[URL]] or ōþer ærn hwilc āmearcaþ [[hyperlink]] ende oþþe brúcung' 54798 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈlɪŋk/ ===Wordstǣr=== Of Middelenglisce ''[[linke]]'' ===Nama=== '''link''' {{en-noun}} # þæt [[URL]] or ōþer ærn hwilc āmearcaþ [[hyperlink]] ende oþþe brúcung ew54ejwlz4eb9z4xkwe68nfwjizufre or 0 8033 54799 2026-09-26T02:47:38Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * (unstrang) IPA: /ə(ɹ)/ * (strang) IPA: /ɔː(ɹ)/ ===Féging=== {{en-head|conj}} # oþþe [[Flocc: Nīwenglisc féginga]]' 54799 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * (unstrang) IPA: /ə(ɹ)/ * (strang) IPA: /ɔː(ɹ)/ ===Féging=== {{en-head|conj}} # oþþe [[Flocc: Nīwenglisc féginga]] b1ylrmkx4w6fk0jxdiyj2ghk0f9nwz6 census 0 8034 54800 2026-09-26T02:54:59Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈsɛnsəs/ ===Nama=== {{en-head|noun}} # tómearcodnes [[Flocc: Nīwenglisc nama]]' 54800 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈsɛnsəs/ ===Nama=== {{en-head|noun}} # tómearcodnes [[Flocc: Nīwenglisc nama]] lx6lyz85ac818sqm42b9pyea0j04snf 54801 54800 2026-09-26T02:56:46Z Deadend0914 7211 /* Nama */ 54801 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈsɛnsəs/ ===Nama=== {{en-hēafod|noun}} # tómearcodnes [[Flocc: Nīwenglisc nama]] 6g1736dwfpmfu5qmymxe8r3xjrdpb9f Bysen:en-hēafod 10 8035 54802 2026-09-26T02:57:44Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:en-hēafodword|show}}<!-- --><noinclude>{{documentation}}</noinclude>' 54802 wikitext text/x-wiki {{#invoke:en-hēafodword|show}}<!-- --><noinclude>{{documentation}}</noinclude> hmv6y4tfqidnweurumv3pcf0qulf0qy Module:en-hēafodword 828 8036 54803 2026-09-26T03:07:07Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") lo...' 54803 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_netwerdlicnesa_module = "Module:en-netwerdlicnesa" local heafodword_netwerdlicnesa_module = "Module:heafodword netwerdlicnesa" local heafodword_module = "Module:heafodword" local declinung_netwerdlicnesa_module = "Module:declinung netwerdlicnesa" local parse_netwerdlicnesa_module = "Module:parse netwerdlicnesa" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_netwerdlicnesa_module = "Module:string netwerdlicnesa" local table_module = "Module:table" local netwerdlicnesa_module = "Module:netwerdlicnesa" local yesno_module = "Module:yesno" local iut = require_when_needed(declinung_netwerdlicnesa_module) local put = require_when_needed(parse_netwerdlicnesa_module) local m_heafodword_netwerdlicnesa = require_when_needed(heafodword_netwerdlicnesa_module) local add_links_to_multiword_term = require_when_needed(heafodword_netwerdlicnesa_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_netwerdlicnesa_module, "add_suffix") local apply_link_modifiers = require_when_needed(heafodword_netwerdlicnesa_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(netwerdlicnesa_module, "format_categories") local full_heafodword = require_when_needed(heafodword_module, "full_heafodword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(heafodword_netwerdlicnesa_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_netwerdlicnesa_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_netwerdlicnesa_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_netwerdlicnesa_module, "singularize") local split = require_when_needed(string_netwerdlicnesa_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_netwerdlicnesa_module, "len") local ulower = require_when_needed(string_netwerdlicnesa_module, "lower") local umatch = require_when_needed(string_netwerdlicnesa_module, "match") local u = require_when_needed(string_netwerdlicnesa_module, "char") local ugsub = require_when_needed(string_netwerdlicnesa_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-heafodword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an declinung not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_declinung(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_heafodword_netwerdlicnesa.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed declinunga in `terms` (as parsed by `parse_declinung`) into `data.declinunga`, with label -- `label` and optional accelerator spec `accel`. local function insert_declinung(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_heafodword_netwerdlicnesa.remove_termobj_field_modifiers(termobj) end m_heafodword_netwerdlicnesa.insert_declinung { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the declinunga for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_declinung(data, label, originating_term) m_heafodword_netwerdlicnesa.insert_fixed_declinung { headdata = data, originating_term = originating_term, label = label, } end -- Parse and insert an declinung not requiring additional processing into `data.declinunga`. The raw arguments come -- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the declinunga are given; -- `accel` is the accelerator form, or nil. local function parse_and_insert_declinung(data, args, field, label, accel) m_heafodword_netwerdlicnesa.parse_and_insert_declinung { headdata = data, forms = args[field], paramname = field, splitchar = ",", label = label, accel = accel and {form = accel} or nil, } end -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(heafodword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:heafodword/data").pagename local user_specified_heads = parse_declinung(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, declinunga = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.declinunga, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled without vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_declinung(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_heafodword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_declinung(args, 1) local sups = parse_declinung(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.declinunga, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.declinunga, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along with '?'") end if sups[1] then error("Can't specify superlatives along with '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_declinung(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_declinung(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_declinung() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends with 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_heafodword_netwerdlicnesa.get_special_indicator(comp, "noerror") if special then comp = m_heafodword_netwerdlicnesa.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_declinung() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_heafodword_netwerdlicnesa.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends with 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_heafodword_netwerdlicnesa.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_heafodword_netwerdlicnesa.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_heafodword_netwerdlicnesa.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_declinung(data, outcomps, "<<comparative>>", "comparative") insert_declinung(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends with 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_declinung(args, 1) local function insert_plurale_tantum_declinunga(is_plural_only, originating_label) if args.sg[1] then insert_fixed_declinung(data, "normally plural", originating_label) parse_and_insert_declinung(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_declinung(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_declinung(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("With plurale tantum noun, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_declinunga("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("nouns construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_declinunga(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Uncountable noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("uncountable nouns") -- If plural forms were given explicitly, then show "usually" if plurals[1] then insert_fixed_declinung(data, "usually <<uncountable>>", hyphpl) else insert_fixed_declinung(data, "<<uncountable>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- Usually countable (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_declinung(data, "usually <<countable>>", hashpl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed countable/uncountable noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_declinung(data, "<<countable>> and <<uncountable>>", tildepl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_declinung(data, "plural unknown or uncertain", questionpl) inscat("nouns with unknown or uncertain plurals") if plurals[1] then error("Can't specify explicit plurals along with '?' for unknown/uncertain plural") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_declinung(data, "plural not attested", exclampl) inscat("nouns with unattested plurals") if plurals[1] then error("Can't specify explicit plurals along with '!' for unattested plural") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("uncountable nouns") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("countable nouns") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("nouns with irregular plurals") end if indeclinable then inscat("indeclinable nouns") end insert_declinung(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper noun") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Verb '" .. verb .. "' must have a space in it to use **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("First word '" .. first .. "' must end in a consonant to use **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Verb '" .. pagename .. "' must have a space in it to use *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_declinung(args, {2, "pres_ptc"}) local par3s = parse_declinung(args, {3, "past"}) local par4s = parse_declinung(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along with the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_declinung, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_heafodword_netwerdlicnesa.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default heafodword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_heafodword_netwerdlicnesa.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_heafodword_netwerdlicnesa.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_heafodword_netwerdlicnesa.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_heafodword_netwerdlicnesa.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_heafodword_netwerdlicnesa.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default heafodword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_heafodword_netwerdlicnesa.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_declinung(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_heafodword_netwerdlicnesa.combine_termobj_decorations(canon, arg) m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_heafodword_netwerdlicnesa.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT declinunga ------------------------------------------ insert_declinung(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_declinung(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_declinung(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_declinung(data, pasts, "simple past", "spast") insert_declinung(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms with placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed with "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.declinunga, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export rc4zzpw7h4ajw9jv7jgafpg96owod5d 54804 54803 2026-09-26T03:08:12Z Deadend0914 7211 54804 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Bócere from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_netwerdlicnesa_module = "Module:en-netwerdlicnesa" local heafodword_netwerdlicnesa_module = "Module:heafodword netwerdlicnesa" local heafodword_module = "Module:heafodword" local declinung_netwerdlicnesa_module = "Module:declinung netwerdlicnesa" local parse_netwerdlicnesa_module = "Module:parse netwerdlicnesa" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_netwerdlicnesa_module = "Module:string netwerdlicnesa" local table_module = "Module:table" local netwerdlicnesa_module = "Module:netwerdlicnesa" local yesno_module = "Module:yesno" local iut = require_when_needed(declinung_netwerdlicnesa_module) local put = require_when_needed(parse_netwerdlicnesa_module) local m_heafodword_netwerdlicnesa = require_when_needed(heafodword_netwerdlicnesa_module) local add_links_to_multiword_term = require_when_needed(heafodword_netwerdlicnesa_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_netwerdlicnesa_module, "add_suffix") local apply_link_modifiers = require_when_needed(heafodword_netwerdlicnesa_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(netwerdlicnesa_module, "format_categories") local full_heafodword = require_when_needed(heafodword_module, "full_heafodword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(heafodword_netwerdlicnesa_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_netwerdlicnesa_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_netwerdlicnesa_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_netwerdlicnesa_module, "singularize") local split = require_when_needed(string_netwerdlicnesa_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_netwerdlicnesa_module, "len") local ulower = require_when_needed(string_netwerdlicnesa_module, "lower") local umatch = require_when_needed(string_netwerdlicnesa_module, "match") local u = require_when_needed(string_netwerdlicnesa_module, "char") local ugsub = require_when_needed(string_netwerdlicnesa_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-heafodword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an declinung not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_declinung(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_heafodword_netwerdlicnesa.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed declinunga in `terms` (as parsed by `parse_declinung`) into `data.declinunga`, with label -- `label` and optional accelerator spec `accel`. local function insert_declinung(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_heafodword_netwerdlicnesa.remove_termobj_field_modifiers(termobj) end m_heafodword_netwerdlicnesa.insert_declinung { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the declinunga for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_declinung(data, label, originating_term) m_heafodword_netwerdlicnesa.insert_fixed_declinung { headdata = data, originating_term = originating_term, label = label, } end -- Parse and insert an declinung not requiring additional processing into `data.declinunga`. The raw arguments come -- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the declinunga are given; -- `accel` is the accelerator form, or nil. local function parse_and_insert_declinung(data, args, field, label, accel) m_heafodword_netwerdlicnesa.parse_and_insert_declinung { headdata = data, forms = args[field], paramname = field, splitchar = ",", label = label, accel = accel and {form = accel} or nil, } end -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(heafodword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:heafodword/data").pagename local user_specified_heads = parse_declinung(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, declinunga = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.declinunga, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled without vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_declinung(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_heafodword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_declinung(args, 1) local sups = parse_declinung(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.declinunga, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.declinunga, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along with '?'") end if sups[1] then error("Can't specify superlatives along with '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_declinung(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_declinung(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_declinung() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends with 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_heafodword_netwerdlicnesa.get_special_indicator(comp, "noerror") if special then comp = m_heafodword_netwerdlicnesa.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_declinung() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_heafodword_netwerdlicnesa.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends with 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_heafodword_netwerdlicnesa.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_heafodword_netwerdlicnesa.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_heafodword_netwerdlicnesa.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_declinung(data, outcomps, "<<comparative>>", "comparative") insert_declinung(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends with 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_declinung(args, 1) local function insert_plurale_tantum_declinunga(is_plural_only, originating_label) if args.sg[1] then insert_fixed_declinung(data, "normally plural", originating_label) parse_and_insert_declinung(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_declinung(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_declinung(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("With plurale tantum noun, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_declinunga("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("nouns construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_declinunga(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Uncountable noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("uncountable nouns") -- If plural forms were given explicitly, then show "usually" if plurals[1] then insert_fixed_declinung(data, "usually <<uncountable>>", hyphpl) else insert_fixed_declinung(data, "<<uncountable>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- Usually countable (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_declinung(data, "usually <<countable>>", hashpl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed countable/uncountable noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_declinung(data, "<<countable>> and <<uncountable>>", tildepl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_declinung(data, "plural unknown or uncertain", questionpl) inscat("nouns with unknown or uncertain plurals") if plurals[1] then error("Can't specify explicit plurals along with '?' for unknown/uncertain plural") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_declinung(data, "plural not attested", exclampl) inscat("nouns with unattested plurals") if plurals[1] then error("Can't specify explicit plurals along with '!' for unattested plural") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("uncountable nouns") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("countable nouns") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("nouns with irregular plurals") end if indeclinable then inscat("indeclinable nouns") end insert_declinung(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper noun") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Verb '" .. verb .. "' must have a space in it to use **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("First word '" .. first .. "' must end in a consonant to use **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Verb '" .. pagename .. "' must have a space in it to use *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_declinung(args, {2, "pres_ptc"}) local par3s = parse_declinung(args, {3, "past"}) local par4s = parse_declinung(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along with the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_declinung, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_heafodword_netwerdlicnesa.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default heafodword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_heafodword_netwerdlicnesa.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_heafodword_netwerdlicnesa.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_heafodword_netwerdlicnesa.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_heafodword_netwerdlicnesa.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_heafodword_netwerdlicnesa.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default heafodword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_heafodword_netwerdlicnesa.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_declinung(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_heafodword_netwerdlicnesa.combine_termobj_decorations(canon, arg) m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_heafodword_netwerdlicnesa.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_heafodword_netwerdlicnesa.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT declinunga ------------------------------------------ insert_declinung(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_declinung(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_declinung(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_declinung(data, pasts, "simple past", "spast") insert_declinung(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms with placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed with "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.declinunga, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export gseb2apwvhiuyb6uz4c57pbvdm5mugo